/*
 * Wazuh Module to analyze system vulnerabilities
 * Copyright (C) 2015-2021, Wazuh Inc.
 * January 4, 2018.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#ifndef WIN32

#include "../wmodules.h"
#include "wm_vuln_detector_db.h"
#include "wm_vuln_detector_evr.h"
#include "addagent/manage_agents.h"
#include "wazuh_db/helpers/wdb_global_helpers.h"
#include "wazuh_db/helpers/wdb_agents_helpers.h"
#include <netinet/tcp.h>
#include <openssl/ssl.h>
#include <os_net/os_net.h>

#ifdef WAZUH_UNIT_TESTING
// Remove STATIC qualifier from tests
#define STATIC
#else
#define STATIC static
#endif

#if defined(__MACH__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#define SOL_TCP     6
#endif

STATIC void *wm_vuldet_main(wm_vuldet_t * vuldet);
STATIC void wm_vuldet_destroy(wm_vuldet_t * vuldet);
STATIC int wm_vuldet_run_update(update_node **updates);
STATIC char *wm_vuldet_oval_xml_preparser(char *path, vu_feed dist);
STATIC int wm_vuldet_index_feed(update_node *update);
STATIC int wm_vuldet_fetch_feed(update_node *update, int8_t *need_update);

/**
 * @brief Traverse the list of CVEs within the RHSA, and insert
 * each one of them into the global linked list of vulnerabilities.
 * @param vul_it Vulnerability to analise.
 *
 */
STATIC void wm_vuldet_oval_traverse_rhsa(vulnerability *vul_it);

/**
 * @brief Append a new CVE to the RHSA list of a given vulnerability.
 * @param vul_it Vulnerability to analise.
 * @param cve New CVE.
 *
 */
STATIC void wm_vuldet_oval_append_rhsa(vulnerability *vul_it, char *cve);

/**
 * @brief Copy all the items within the RHSA into a new vulnerability's RHSA list.
 * @param old Old Vulnerability being analised.
 * @param new New Vulnerability being analised.
 *
 */
STATIC void wm_vuldet_oval_copy_rhsa(vulnerability *old, vulnerability *new);

STATIC int wm_vuldet_oval_xml_parser(OS_XML *xml, XML_NODE node, wm_vuldet_db *parsed_oval, update_node *update, vu_logic condition);
STATIC int wm_vuldet_json_parser(char *json_path, wm_vuldet_db *uparsed_vulnerabilities, update_node *update);

/**
 * @brief Add a new vulnerability to the linked list.
 * @param ctrl_block Parsed oval data strucure.
 *
 */
STATIC void wm_vuldet_add_oval_vulnerability(wm_vuldet_db *ctrl_block);
STATIC void wm_vuldet_add_vulnerability_info(wm_vuldet_db *ctrl_block);

/**
 * @brief Clean the CVEs linked list data structure.
 * @param ctrl_block Parsed oval data strucure.
 *
 */
STATIC void wm_vuldet_clean_vulnerability_info(wm_vuldet_db *ctrl_block);

STATIC references *wm_vuldet_extract_advisories(cJSON *advisories);
STATIC int wm_vuldet_check_db();

/**
 * @brief Insert the CVEs information, fetched from the feeds, into VULNERABILITIES_INFO table.
 * @param parsed_oval The feed's information.
 * @param db SQLite3 Database pointer.
 * @param stmt SQL query statement pointer.
 * @return 0 if success.
 */
STATIC int wm_vuldet_insert_cve_info(wm_vuldet_db *parsed_oval, sqlite3 *db, sqlite3_stmt *stmt);

STATIC int wm_vuldet_insert(wm_vuldet_db *parsed_oval, update_node *update);
STATIC int wm_vuldet_remove_target_table(sqlite3 *db, char *TABLE, const char *target);

/**
 * @brief Set and validate the required URL for the feed.
 * Currently Debian, Ubuntu and RedHat are supported.
 * @param update Update structure of the feed being analyzed.
 * @return False if an unexpected feed is analyzed; True othewise.
 */
STATIC bool wm_vuldet_set_feed_update_url(update_node *update);

/**
 * @brief Get the Debian status feed to check the status of the vulnerabilities.
 * @param update Update structure of the feed being analyzed.
 * @return Parsed JSON on success, NULL otherwise.
 */
STATIC cJSON *wm_vuldet_get_debian_status_feed(update_node *update);

/**
 * @brief Insert the Debian vulnerable packages into the DB.
 * @param db Pointer to the CVE DB.
 * @param target Debian OS target.
 * @param update Update structure of the feed being analyzed.
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_index_debian(sqlite3 *db, const char *target, update_node *update);

/**
 * @brief For an specific agent, copy all installed packages' information from SYS_PROGRAM to the AGENTS table.
 * @param agent_software Pointer to an agent node.
 * @param db The agent sqlite Data Base.
 * @param ignore_time The configured scan ignore time to determine if a full scan is required.
 * @param request The request type that was used. Full or partial.
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_get_software_info(agent_software *agent, sqlite3 *db, time_t ignore_time, int* request);

/**
 * @brief Search for known vulnerabilities (NVD and OVAL feeds), and report all found CVEs.
 * @param agent_software Pointer to an agent node.
 * @param flags Flag list for Windows agents
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_report_agent_vulnerabilities(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags);

/**
 * @brief Traverse the agents linked list, gather the installed packages and search
 * for known vulnerabilities.
 * @param agents_software Pointer to the new linked list.
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_check_agent_vulnerabilities(agent_software *agents, wm_vuldet_flags *flags, time_t ignore_time);

/**
 * @brief Discard any installed Linux kernel package which is not running.
 * @param agent Agent being analyzed.
 * @param name Name of the package.
 * @param version Version of the package.
 * @param arch Architecture of the package.
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_discard_kernel_package(agent_software *agent, const char *name, const char *version, const char *arch);

/**
 * @brief Discard false positives in CVEs with multiple entries when they differ in their vulnerability diagnosis.
 * @param db The agent's sqlite database.
 * @param agents_it The software struct from the agent being analyzed.
 * @param cve_id The CVE being analyzed.
 * @param pkg_version The version from the package being analyzed.
 * @param pkg_name The name from the package being analyzed.
 * @param vertype The version comparisor type.
 * @return The return code from the version comparisor (VU_NOT_VULNERABLE, VU_NOT_FIXED, VU_ERROR_CMP, ...).
 */
STATIC int wm_vuldet_oval_discard_mismatching_cve_entries(sqlite3 *db, agent_software *agents_it, char *cve_id, char *pkg_version, char *pkg_name, version_type vertype);

STATIC int wm_vuldet_create_file(const char *path, const char *source);
STATIC int wm_vuldet_check_update_period(update_node *upd);
STATIC int wm_vuldet_update_feed(update_node *upd, int8_t *updated);
STATIC int wm_vuldet_sync_feed(update_node *upd, int8_t *need_update);
STATIC int wm_vuldet_check_feed(update_node *upd, int8_t *updated);
STATIC vu_feed wm_vuldet_set_allowed_feed(const char *os_name, const char *os_version, update_node **updates, vu_feed *agent_dist);

/**
 * @brief Initialize the main structure(linked list) of all Syscollector agents
 * for later analysis.
 *
 * @param node_name The node name of the manager in cluster.
 * @param agents_software Pointer to the new linked list.
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_set_agents_info(const char *node_name, agent_software **agents_software, update_node **updates);

/**
 * @brief Compare two packages structures (src_name, bin_name, version and arch).
 *
 * @return 0 if equal, -1 otherwise.
 */
STATIC int wm_vuldet_compare_pkg(cve_vuln_pkg *Pkg1, cve_vuln_pkg *Pkg2);

STATIC cJSON *wm_vuldet_dump(const wm_vuldet_t *vuldet);
STATIC char *wm_vuldet_build_url(char *pattern, char *value);
STATIC void wm_vuldet_adapt_title(char *title, char *cve);
STATIC char *vu_get_version();
STATIC int wm_vuldet_fetch_oval(update_node *update, char *repo);
STATIC int wm_vuldet_oval_process(update_node *update, char *path, wm_vuldet_db *parsed_vulnerabilities);
STATIC int wm_vuldet_json_rh_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities);
STATIC int wm_vuldet_json_arch_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities);
STATIC int wm_vuldet_db_empty(sqlite3 *db, vu_feed version);
STATIC int wm_vuldet_nvd_empty();
STATIC const char *wm_vuldet_get_unified_severity(char *severity);
STATIC int wm_vuldet_generate_os_cpe(sqlite3 *db, agent_software *agent, int *min_cpe_index, cpe_list **node_list);
STATIC int wm_vuldet_generate_os_and_kernel_package(sqlite3 *db, agent_software *agent);
STATIC int wm_vuldet_generate_win_cpe(agent_software *agent);
STATIC vu_feed wm_vuldet_decode_win_os(char *os_raw);
STATIC int wm_vuldet_insert_agent_data(sqlite3 *db, agent_software *agent, int *cpe_index, const char *vendor, const char *product, const char *source, const char *version, const char *src_version, const char *arch, cpe *ag_cpe, cpe_list **node_list);
STATIC void wm_vuldet_free_agent_software(agent_software *agent);

/**
 * @brief Downloads the MSU feed.
 * @param update Update structure of the feed being analyzed.
 * @param repo By default is set to NULL; however, if the user specifies another
 * url (tag <url>), this one will be used.
 * @return 0 if it was successfully downloaded, VU_INV_FEED if it failed,
 *  and VU_NOT_NEED_UPDATE if it's not required.
 */
STATIC int wm_vuldet_fetch_MSU(update_node *update, char *repo);

/**
 * @brief Checks whether the MSU must be updated or not.
 * This is done by checking the metadata.
 * @param update Update structure of the feed being analyzed.
 * @return True if it must be updated, false otherwise.
 */
STATIC bool wm_vuldet_update_MSU(update_node *update);

/**
 * @brief Downloads the msu.meta file.
 * @param update Update structure of the feed being analyzed.
 * @return Structure that holds all the MSU's metadata. NULL otherwise.
 */
STATIC feed_metadata * wm_vuldet_fetch_MSU_metadata(update_node *update);

/**
 * @brief Parse the msu.meta file and get the MSU's metadata.
 * @param path Path of the msu.meta file.
 * @return Structure that holds all the MSU's metadata. NULL otherwise.
 */
STATIC feed_metadata * wm_vuldet_parse_MSU_metadata(char *path);

/**
 * @brief Insert or update the MSU entry in the METADATA table.
 * @param msu Structure that holds all the MSU's metadata.
 * @param update Update structure of the feed being analyzed.
 * @return 0 if success, -1 otherwise.
 */
STATIC int wm_insert_MSU_metadata(feed_metadata *msu, update_node *update);

/**
 * @brief Checks whether the MSU table is populated or not.
 * @param db CVE Database with MSU information.
 * @return true on success, false otherwise.
 */
STATIC bool wm_vuldet_check_enabled_msu(sqlite3 *db);

STATIC int wm_vuldet_fetch_redhat(update_node *update);
STATIC int wm_vuldet_fetch_arch(update_node *update, char * repo);
STATIC int wm_vuldet_fetch_wazuh_cpe(update_node *update);
STATIC int wm_vuldet_json_wcpe_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities);
STATIC int wm_vuldet_json_dic_parser(cJSON *json_feed, vu_cpe_dic *wcpes);
STATIC int wm_vuldet_fill_dic_section(cJSON *json_feed, vu_cpe_dic_section **wcpes);
STATIC unsigned int wm_vuldet_fill_action_mask(cJSON *json_feed);
STATIC int wm_vuldet_json_msu_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities);
STATIC int wm_vuldet_insert_cpe_dic(sqlite3 *db, vu_cpe_dic *w_cpes);
STATIC int wm_vuldet_insert_cpe_dic_array(sqlite3 *db, vu_query query, int id, vu_cpe_dic_section *section);
STATIC vu_cpe_dic_node *wm_vuldet_free_dic_node(vu_cpe_dic_node *dic_node);
STATIC int wm_vuldet_insert_MSU(sqlite3 *db, vu_msu_entries *msu);
STATIC vu_msu_vul_entry *wm_vuldet_free_msu_vul_node(vu_msu_vul_entry *node);
STATIC vu_msu_dep_entry *wm_vuldet_free_msu_dep_node(vu_msu_dep_entry *node);
STATIC int wm_vuldet_clean_agent_cpes(char *agent_id);
STATIC int wm_vuldet_get_oswindows_info(agent_software *agent);
STATIC int wm_vuldet_get_os_unix_info(agent_software *agent);
STATIC int wm_vuldet_request_hotfixes(sqlite3 *db, char *agent_id);
STATIC void wm_vuldet_reset_tables(sqlite3 *db);
STATIC int wm_vuldet_index_json(wm_vuldet_db *parsed_vulnerabilities, update_node *update, char *path, char multi_path);
STATIC int wm_vuldet_index_nvd(sqlite3 *db, update_node *upd, nvd_vulnerability *nvd_it);
STATIC int wm_vuldet_clean_rh(sqlite3 *db);
STATIC int wm_vuldet_clean_wcpe(sqlite3 *db);
STATIC void wm_vuldet_get_package_os(const char *version, const char **os_major, char **os_minor);
STATIC void wm_vuldet_set_subversion(char *version, char **os_minor);
STATIC cJSON *wm_vuldet_json_fread(char *json_file);
STATIC cJSON *wm_vuldet_get_cvss(const char *scoring_vector);
STATIC int wm_vuldet_get_term_condition(char *i_term, char **term, char **comp_field, char **condition);
STATIC void wm_vuldet_run_scan(wm_vuldet_t *vuldet);
STATIC void wm_vuldet_run_sleep(wm_vuldet_t *vuldet);
STATIC void wm_vuldet_init(wm_vuldet_t *vuldet);
STATIC int wm_vuldet_select_scan_type(char *agent_id, time_t ignore_time);
STATIC void wm_vuldet_update_last_scan(char *agent_id);
STATIC char *wm_vuldet_get_hotfix_scan(char *agent_id);
STATIC int wm_vuldet_get_last_software_scan(char *agent_id, char scan_id[OS_SIZE_128]);
STATIC char *wm_vuldet_normalize_date(char **date);
STATIC int wm_vulndet_insert_msu_vul_entry(sqlite3 *db, vu_msu_vul_entry *vul);
STATIC void wm_vuldet_json_msu_parser_deps(cJSON *json_feed, vu_msu_dep_entry **msu);
STATIC void wm_vuldet_json_msu_parser_vuln(cJSON *json_feed, vu_msu_vul_entry **msu);
STATIC int wm_vulndet_insert_msu_dep_entry(sqlite3 *db, vu_msu_dep_entry *dep);
STATIC void wm_vuln_check_msu_type(vu_msu_vul_entry *msu, cJSON *patchs);

/**
 * @brief Compares the package's vendor with the official vendors.
 *
 * @param vendor the package's vendor
 * @return 0 if the vendor matches with an official vendor or 1 if it is a external vendor.
 */
STATIC int wm_vuldet_compare_vendors(char * vendor);

/**
 * @brief Truncate the minor target for CentOS packages.
 *
 * @param revision a pointer to the package revision
 */
STATIC void wm_vuldel_truncate_revision(char * revision);

int wdb_vuldet_sock = -1;
int *vu_queue;
// Define time to sleep between messages sent
int usec;

const wm_context WM_VULNDETECTOR_CONTEXT = {
    "vulnerability-detector",
    (wm_routine)wm_vuldet_main,
    (wm_routine)(void *)wm_vuldet_destroy,
    (cJSON * (*)(const void *))wm_vuldet_dump,
    NULL,
    NULL
};

const char *version_null = "0:0";

static const char *PR_UBUNTU = "ubuntu_linux";
static const char *PR_DEBIAN = "debian_linux";
static const char *PR_REDHAT = "enterprise_linux";
static const char *PR_ARCH = "arch_linux";
static const char *PR_KERNEL = "linux_kernel";
static const char *V_UBUNTU = "canonical";
static const char *V_DEBIAN = "debian";
static const char *V_REDHAT = "redhat";
static const char *V_ARCH = "archlinux";
static const char *V_KERNEL = "linux";

const char *vu_vendor_list_ubuntu_debian[] = {
    "canonical",
    "ubuntu",
    "debian"
};

const char *vu_vendor_list_redhat[] = {
    "Red Hat, Inc.",
    "CentOS"
};

const char *vu_vendor_list_arch[] = {
    "Arch Linux"
};

const char *vu_package_dist[] = {
    ".el8",
    ".el7",
    ".el6",
    ".el5"
};

const char *vu_feed_tag[] = {
    "UBUNTU",
    "CANONICAL",
    "DEBIAN",
    "REDHAT",
    "JREDHAT",
    "CENTOS",
    "ARCH",
    "WIN",
    // Ubuntu versions
    "TRUSTY",
    "XENIAL",
    "BIONIC",
    "FOCAL",
    // Debian versions
    "STRETCH",
    "BUSTER",
    // RedHat versions
    "RHEL5",
    "RHEL6",
    "RHEL7",
    "RHEL8",
    // NVD
    "NVD",
    "CPED",
    // WINDOWS
    "WS2003",
    "WS2003R2",
    "WXP",
    "WVISTA",
    "W7",
    "W8",
    "W81",
    "W10",
    "WS2008",
    "WS2008R2",
    "WS2012",
    "WS2012R2",
    "WS2016",
    "WS2019",
    "WS2022",
    // OTHER
    "CPEW",
    "MSU",
    "MAC",
    "UNKNOWN"
};

const char *vu_feed_ext[] = {
    "Ubuntu",
    "Canonical",
    "Debian",
    "Red Hat Enterprise Linux",
    "JSON Red Hat Enterprise Linux",
    "CentOS",
    "Arch Linux",
    "Microsoft Windows",
    // Ubuntu versions
    "Ubuntu Trusty",
    "Ubuntu Xenial",
    "Ubuntu Bionic",
    "Ubuntu Focal",
    // Debian versions
    "Debian Stretch",
    "Debian Buster",
    // RedHat versions
    "Red Hat Enterprise Linux 5",
    "Red Hat Enterprise Linux 6",
    "Red Hat Enterprise Linux 7",
    "Red Hat Enterprise Linux 8",
    // NVD
    "National Vulnerability Database",
    "Common Platform Enumeration Dictionary",
    // WINDOWS
    "Windows Server 2003",
    "Windows Server 2003 R2",
    "Windows XP",
    "Windows Vista",
    "Windows 7",
    "Windows 8",
    "Windows 8.1",
    "Windows 10",
    "Windows Server 2008",
    "Windows Server 2008 R2",
    "Windows Server 2012",
    "Windows Server 2012 R2",
    "Windows Server 2016",
    "Windows Server 2019",
    "Windows Server 2022",
    // OTHER
    "Wazuh CPE dictionary",
    "Microsoft Security Update",
    "darwin",
    "Unknown OS"
};

const char *vu_package_comp[] = {
    "less than",
    "less than or equal",
    "greater than",
    "greater than or equal",
    "equals",
    "not equal",
    "exists"
};

const char *vu_severities[] = {
    "Low",
    "Medium",
    "Moderate",
    "Unknown",
    "High",
    "Important",
    "Critical",
    "None",
    "Negligible",
    "Untriaged",
    "-"
};

const char *vu_cpe_dic_option[] = {
    "replace_vendor",
    "replace_product",
    "replace_vendor_if_matches",
    "replace_product_if_matches",
    "set_version_if_matches",
    "replace_sw_edition_if_product_matches",
    "replace_msu_name_if_version_matches",
    "ignore",
    "check_hotfix",
    "replace_msu_name",
    "set_version_if_product_matches",
    "set_targethw_if_product_matches",
    "set_version_only_if_product_matches",
    "set_targethw_only_if_product_matches"
};

vu_feed wm_vuldet_set_allowed_feed(const char *os_name, const char *os_version, update_node **updates, vu_feed *agent_dist) {
    vu_feed retval = FEED_UNKNOWN;
    int i;

    for (i = 0; i < OS_SUPP_SIZE; i++) {
        if (updates[i]) {
            if (wm_vuldet_is_single_provider(updates[i]->dist_ref)) {
                if (updates[i]->allowed_os_name) {
                    int j;
                    char *allowed_os;
                    char *allowed_ver;
                    for (allowed_os = *updates[i]->allowed_os_name, allowed_ver = *updates[i]->allowed_os_ver, j = 0; allowed_os; ++j) {
                        if (strcasestr(os_name, allowed_os) && strcasestr(os_version, allowed_ver)) {
                            retval = updates[i]->dist_tag_ref;
                            *agent_dist = updates[i]->dist_ref;
                            i = OS_SUPP_SIZE;
                            break;
                        }
                        allowed_os = updates[i]->allowed_os_name[j];
                        allowed_ver = updates[i]->allowed_os_ver[j];
                    }
                }
            }
        }
    }

    return retval;
}

int wm_vuldet_check_update_period(update_node *upd) {
    return upd && (!upd->last_update || (upd->interval != WM_VULNDETECTOR_ONLY_ONE_UPD && (upd->last_update + (time_t) upd->interval) < time(NULL)));
}
int wm_vuldet_sync_feed(update_node *upd, int8_t *need_update) {
    return wm_vuldet_fetch_feed(upd, need_update) == OS_INVALID || (*need_update && wm_vuldet_index_feed(upd));
}

int wm_vuldet_create_file(const char *path, const char *source) {
    const char *ROOT = "root";
    const char *sql;
    const char *tail;
    sqlite3 *db;
    sqlite3_stmt *stmt = NULL;
    int result;
    uid_t uid;
    gid_t gid;

    // Open the database file, or create it
    if (sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CREATE_DB_ERROR);
        return wm_vuldet_sql_error(db, stmt);
    }

    // Load the tables schema
    for (sql = source; sql && *sql; sql = tail) {
        if (wm_vuldet_prepare(db, sql, -1, &stmt, &tail) != SQLITE_OK) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_CREATE_DB_ERROR);
            return wm_vuldet_sql_error(db, stmt);
        }

        result = wm_vuldet_step(stmt);

        switch (result) {
        case SQLITE_MISUSE:
        case SQLITE_ROW:
        case SQLITE_DONE:
            break;
        default:
            return wm_vuldet_sql_error(db, stmt);
        }

        wdb_finalize(stmt);
    }

    sqlite3_close_v2(db);

    uid = Privsep_GetUser(ROOT);
    gid = Privsep_GetGroup(GROUPGLOBAL);

    if (uid == (uid_t) - 1 || gid == (gid_t) - 1) {
        mterror(WM_VULNDETECTOR_LOGTAG, USER_ERROR, ROOT, GROUPGLOBAL, strerror(errno), errno);
        return OS_INVALID;
    }

    if (chown(path, uid, gid) < 0) {
        mterror(WM_VULNDETECTOR_LOGTAG, CHOWN_ERROR, path, errno, strerror(errno));
        return OS_INVALID;
    }

    if (chmod(path, 0660) < 0) {
        mterror(WM_VULNDETECTOR_LOGTAG, CHMOD_ERROR, path, errno, strerror(errno));
        return OS_INVALID;
    }

    return 0;
}

int wm_vuldet_prepare(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **stmt, const char **pzTail) {
    int attempts;
    int result;
    for (attempts = 0; (result = sqlite3_prepare_v2(db, zSql, nByte, stmt, pzTail)) == SQLITE_BUSY; attempts++) {
        if (attempts == MAX_SQL_ATTEMPTS) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_MAX_ACC_EXC);
            return OS_INVALID;
        }
    }
    return result;
}

int wm_vuldet_step(sqlite3_stmt *stmt) {
    int attempts;
    int result;
    for (attempts = 0; (result = sqlite3_step(stmt)) == SQLITE_BUSY; attempts++) {
        if (attempts == MAX_SQL_ATTEMPTS) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_MAX_ACC_EXC);
            return OS_INVALID;
        }
    }
    return result;
}

/*
* This function should be taken as the version comparison entry point
*/
int wm_checks_package_vulnerability(char *version_a, const char *operation, const char *version_b, version_type vertype) {
    int size;
    int epoch, c_epoch;
    char version_cl[KEY_SIZE];
    char cversion_cl[KEY_SIZE];
    char *version_it, *release_it;
    char *cversion_it, *crelease_it;
    struct pkg_version *package_version;
    struct pkg_version *package_feed_version;
    enum pkg_relation feed_condition;
    int8_t ret = 0;

    if (version_b && strcmp(version_b, version_null)) {
        // Copy the original values
        if (size = snprintf(version_cl, KEY_SIZE, "%s", version_a), size >= KEY_SIZE) {
            return OS_INVALID;
        }
        if (size = snprintf(cversion_cl, KEY_SIZE, "%s", version_b), size >= KEY_SIZE) {
            return OS_INVALID;
        }

        // Check EPOCH
        // If the epoch is not present in the version and we are comparing NVD versions
        // we assign -1 to indicate that it shouldn't be compared
        if (version_it = strchr(version_cl, ':'), version_it) {
            *(version_it++) = '\0';
            epoch = strtol(version_cl, NULL, 10);
        } else {
            version_it = version_cl;
            epoch = (VER_TYPE_NVD != vertype) ? 0 : -1;
        }
        if (cversion_it = strchr(cversion_cl, ':'), cversion_it) {
            *(cversion_it++) = '\0';
            c_epoch = strtol(cversion_cl, NULL, 10);
        } else {
            cversion_it = cversion_cl;
            c_epoch = (VER_TYPE_NVD != vertype) ? 0 : -1;
        }

        // Separate the version from the revision
        if (release_it = strchr(version_it, '-'), release_it) {
            if (*(release_it++) = '\0', *release_it == '\0') {
                release_it = NULL;
            }
        }

        if (crelease_it = strchr(cversion_it, '-'), crelease_it) {
            if (*(crelease_it++) = '\0', *crelease_it == '\0') {
                crelease_it = NULL;
            }
        }

        // When evaluating CentOS package, the minor target part cannot be compared so it is truncated
        if (vertype == VER_TYPE_RPM_CENTOS) {
            wm_vuldel_truncate_revision(release_it);
            wm_vuldel_truncate_revision(crelease_it);
        }

        os_calloc(1, sizeof(struct pkg_version), package_version);
        os_calloc(1, sizeof(struct pkg_version), package_feed_version);
        package_version->epoch = epoch;
        package_version->version = version_it;
        package_version->revision = release_it ? release_it : "0";
        package_feed_version->epoch = c_epoch;
        package_feed_version->version = cversion_it;
        package_feed_version->revision = crelease_it ? crelease_it : "0";

        if (!strcmp(operation, vu_package_comp[VU_COMP_L])) {
            feed_condition = PKG_RELATION_LT;
        } else if (!strcmp(operation, vu_package_comp[VU_COMP_LE])) {
            feed_condition = PKG_RELATION_LE;
        } else if (!strcmp(operation, vu_package_comp[VU_COMP_G])) {
            feed_condition = PKG_RELATION_GT;
        } else if (!strcmp(operation, vu_package_comp[VU_COMP_GE])) {
            feed_condition = PKG_RELATION_GE;
        } else if (!strcmp(operation, vu_package_comp[VU_COMP_EQ])) {
            feed_condition = PKG_RELATION_EQ;
        } else {
            ret = VU_ERROR_CMP;
            goto clean;
        }

        // This sanity is included since you cannot always expect a revision to come,
        // since it is a data provided by the agent.
        if ((vertype == VER_TYPE_RPM_CENTOS || vertype == VER_TYPE_RPM) && (NULL == package_version->version)) {
            ret = VU_ERROR_CMP;
            goto clean;
        }

        if(pkg_version_relate(package_version, feed_condition, package_feed_version, vertype)) {
            ret = VU_VULNERABLE;
        } else {
            ret = VU_NOT_VULNERABLE;
        }

    clean:
        os_free(package_version);
        os_free(package_feed_version);
        return ret;
    }

    return VU_NOT_FIXED;
}

char *wm_vuldet_build_url(char *pattern, char *value) {
    size_t size;
    char *retval;

    os_calloc(VUL_BUILD_REF_MAX + 1, sizeof(char), retval);
    size = snprintf(retval, VUL_BUILD_REF_MAX, pattern, value);
    os_realloc(retval, size + 1, retval);

    return retval;
}

int wm_vuldet_compare_pkg(cve_vuln_pkg *Pkg1, cve_vuln_pkg *Pkg2) {

    if (!(!Pkg1->vendor && !Pkg2->vendor) &&
        !(Pkg1->vendor && Pkg2->vendor &&
        !strcmp(Pkg1->vendor, Pkg2->vendor))) { // Vendor
        return -1;
    }

    if (!(!Pkg1->src_name && !Pkg2->src_name) &&
        !(Pkg1->src_name && Pkg2->src_name &&
        !strcmp(Pkg1->src_name, Pkg2->src_name))) { // Source name
        return -1;
    }

    if (!(!Pkg1->bin_name && !Pkg2->bin_name) &&
        !(Pkg1->bin_name && Pkg2->bin_name &&
        !strcmp(Pkg1->bin_name, Pkg2->bin_name))) { // Binary name
        return -1;
    }

    if (!(!Pkg1->version && !Pkg2->version) &&
        !(Pkg1->version && Pkg2->version &&
        !strcmp(Pkg1->version, Pkg2->version))) { // Version
        return -1;
    }

    /* If only the architecture differs and one of them is 'noarch',
    *  label them as equals too.  */
    if (!(!Pkg1->arch && !Pkg2->arch) && // Architecture
        (Pkg1->arch && Pkg2->arch && strcmp("noarch", Pkg1->arch) &&
        strcmp("noarch", Pkg2->arch) && strcmp(Pkg1->arch, Pkg2->arch))) {
        return -1;
    }

    if (Pkg1->nvd_cond && Pkg2->nvd_cond &&
        Pkg1->nvd_cond->id != Pkg2->nvd_cond->id) { // ID from table NVD_CVE_MATCH
        return -1;
    }

    return 0;
}

int wm_vuldet_add_cve_node(cve_vuln_pkg *newPkg, const char *cve, OSHash *cve_table) {
    int status = 0;
    int found = 0;
    cve_vuln_pkg *lastPkg = NULL;
    cve_vuln_pkg *tmpPkg  = NULL;

    if (status = OSHash_Add(cve_table, cve, (void *)newPkg), !status) {
        return -1;
    }

    // If the cve node already exists, add the pkg to the end of the linked list.
    if (status == 1) {
        if (tmpPkg = (cve_vuln_pkg *)OSHash_Get(cve_table, cve), !tmpPkg) {
            return -1;
        }

        do {
            if (!wm_vuldet_compare_pkg(tmpPkg, newPkg)) { // Same package?
                if (!tmpPkg->nvd_cond && newPkg->nvd_cond) {
                    os_calloc(1, sizeof(cve_vuln_cond_NVD), tmpPkg->nvd_cond);
                    memcpy(tmpPkg->nvd_cond, newPkg->nvd_cond, sizeof(cve_vuln_cond_NVD));

                    if (newPkg->nvd_cond->start_version) {
                        os_strdup(newPkg->nvd_cond->start_version, tmpPkg->nvd_cond->start_version);
                    }
                    if (newPkg->nvd_cond->end_version) {
                        os_strdup(newPkg->nvd_cond->end_version, tmpPkg->nvd_cond->end_version);
                    }
                    if (newPkg->nvd_cond->operator) {
                        os_strdup(newPkg->nvd_cond->operator, tmpPkg->nvd_cond->operator);
                    }
                }

                if (!tmpPkg->vuln_cond && newPkg->vuln_cond) {
                    os_calloc(1, sizeof(cve_vuln_cond), tmpPkg->vuln_cond);
                    memcpy(tmpPkg->vuln_cond, newPkg->vuln_cond, sizeof(cve_vuln_cond));

                    if (newPkg->vuln_cond->state) {
                        os_strdup(newPkg->vuln_cond->state, tmpPkg->vuln_cond->state);
                    }
                    if (newPkg->vuln_cond->operation) {
                        os_strdup(newPkg->vuln_cond->operation, tmpPkg->vuln_cond->operation);
                    }
                    if (newPkg->vuln_cond->operation_value) {
                        os_strdup(newPkg->vuln_cond->operation_value, tmpPkg->vuln_cond->operation_value);
                    }
                    if (newPkg->vuln_cond->condition) {
                        os_strdup(newPkg->vuln_cond->condition, tmpPkg->vuln_cond->condition);
                    }
                }

                tmpPkg->feed |= newPkg->feed; // Update the feed field with the new source.

                found = 1; // Package duplicated
            }
            lastPkg = tmpPkg; // Save last known entry within the linked list.
            tmpPkg = tmpPkg->next;
        } while (tmpPkg);

        if (!found) {
            lastPkg->next = newPkg;
        }
    }

    return found;
}

void wm_vuldet_free_cve_node(void *data){
    if (data) {
        cve_vuln_pkg *pkg  = (cve_vuln_pkg *) data;
        cve_vuln_pkg *next = NULL;
        do { // Free each package in the linked list.
            next = pkg->next;
            os_free(pkg->vendor);
            os_free(pkg->bin_name);
            os_free(pkg->src_name);
            os_free(pkg->arch);
            os_free(pkg->version);

            if (pkg->nvd_cond) {
                os_free(pkg->nvd_cond->operator);
                os_free(pkg->nvd_cond->end_version);
                os_free(pkg->nvd_cond->start_version);
                os_free(pkg->nvd_cond);
            }

            if (pkg->vuln_cond) {
                os_free(pkg->vuln_cond->state);
                os_free(pkg->vuln_cond->operation);
                os_free(pkg->vuln_cond->operation_value);
                os_free(pkg->vuln_cond->condition);
                os_free(pkg->vuln_cond);
            }
            os_free(pkg);

            pkg = next;
        } while (pkg);
    }
}

void wm_vuldet_free_report(vu_report *report) {
    if (report == NULL) {
        return;
    }

    os_free(report->cve);
    os_free(report->title);
    os_free(report->rationale);
    os_free(report->published);
    os_free(report->updated);
    os_free(report->assigner);
    os_free(report->cve_version);
    // Impact
    os_free(report->severity);
    wm_vuldet_free_cv_scoring_system(report->cvss2);
    wm_vuldet_free_cv_scoring_system(report->cvss3);
    os_free(report->cwe);
    // References
    free_strarray(report->advisories);
    free_strarray(report->bugzilla_references);
    free_strarray(report->references);
    free_strarray(report->ref_sources);
    // Reported software
    os_free(report->software);
    os_free(report->source);
    os_free(report->generated_cpe);
    os_free(report->version);
    os_free(report->operation);
    os_free(report->operation_value);
    os_free(report->condition);
    os_free(report->arch);
    // Agent data
    os_free(report->agent_id);
    os_free(report->agent_name);
    os_free(report->agent_ip);
    // Metadata
    os_free(report);
}

void wm_vuldet_build_nvd_report_condition(cve_vuln_cond_NVD *nvd_cond, vu_report *report) {

    report->pending = nvd_cond->end_version ? 0 : 1;
    // building condition
    if (nvd_cond->start_version && nvd_cond->end_version) {
        const char* start = "Package";
        const char* condition1 = (nvd_cond->start_operation == START_INCLUDED) ? "greater or equal than" : "greater than";
        const char* middle = "and";
        const char* condition2 = (nvd_cond->end_operation == END_INCLUDED) ? "less or equal than" : "less than";
        size_t size = strlen(start) + 1 + strlen(condition1) + 1 + strlen(nvd_cond->start_version) + 1 +
                        strlen(middle) + 1 + strlen(condition2) + 1 + strlen(nvd_cond->end_version);
        os_calloc(size + 1, sizeof(char), report->condition);
        sprintf(report->condition, "%s %s %s %s %s %s",
        start, condition1, nvd_cond->start_version,
        middle, condition2, nvd_cond->end_version);
    }
    else if (nvd_cond->start_version) {
        const char* start = "Package";
        const char* condition = (nvd_cond->start_operation == START_INCLUDED) ? "greater or equal than" : "greater than";
        size_t size = strlen(start) + 1 + strlen(condition) + 1 + strlen(nvd_cond->start_version);
        os_calloc(size + 1, sizeof(char), report->condition);
        sprintf(report->condition, "%s %s %s",
        start, condition, nvd_cond->start_version);
    }
    else if (nvd_cond->end_version) {
        const char* start = "Package";
        const char* condition = (nvd_cond->end_operation == END_INCLUDED) ? "less or equal than" : "less than";
        size_t size = strlen(start) + 1 + strlen(condition) + 1 + strlen(nvd_cond->end_version);
        os_calloc(size + 1, sizeof(char), report->condition);
        sprintf(report->condition, "%s %s %s",
        start, condition, nvd_cond->end_version);
    }
    else {
        const char* text = "Package matches a vulnerable version";
        os_calloc(strlen(text) + 1, sizeof(char), report->condition);
        sprintf(report->condition, "%s", text);
    }

    return;
}

int wm_vuldet_fill_report_nvd_cve_info(sqlite3 *db, sqlite3_stmt *stmt, vu_report *report, int *cve_table_id) {
    // CVE info query
    if (wm_vuldet_prepare(db, vu_queries[VU_GET_CVE_INFO_FILTER_CVE], -1, &stmt, NULL) != SQLITE_OK) {
        return SQLITE_ERROR;
    }
    sqlite3_bind_text(stmt, 1, report->cve, -1, NULL);

    int step_result = wm_vuldet_step(stmt);
    if (SQLITE_ROW == step_result) {
        *cve_table_id = atoi((char *)sqlite3_column_text(stmt, 0));
        char *cwe = (char *)sqlite3_column_text(stmt, 1);
        char *assigner = (char *)sqlite3_column_text(stmt, 2);
        char *rationale = (char *)sqlite3_column_text(stmt, 3);
        char *cve_version = (char *)sqlite3_column_text(stmt, 4);
        char *published = (char *)sqlite3_column_text(stmt, 5);
        char *updated = (char *)sqlite3_column_text(stmt, 6);

        w_strdup(cwe, report->cwe);
        w_strdup(assigner, report->assigner);
        w_strdup(rationale, report->rationale);
        w_strdup(cve_version, report->cve_version);
        w_strdup(published, report->published);
        w_strdup(updated, report->updated);
    } else if (SQLITE_DONE != step_result){
        return SQLITE_ERROR;
    }

    wdb_finalize(stmt);

    return SQLITE_OK;
}

int wm_vuldet_fill_report_nvd_references(sqlite3 *db, sqlite3_stmt *stmt, int cve_table_id, vu_report *report) {
    //Query for references
    if (wm_vuldet_prepare(db, vu_queries[VU_GET_REFERENCE], -1, &stmt, NULL) != SQLITE_OK) {
        return SQLITE_ERROR;
    }
    sqlite3_bind_int(stmt, 1, cve_table_id);

    int ref_count = 0;
    int step_result = 0;
    while (step_result = wm_vuldet_step(stmt), (SQLITE_ROW == step_result)) {
        char *url = (char *)sqlite3_column_text(stmt, 0);
        char *ref_source = (char *)sqlite3_column_text(stmt, 1);

        os_realloc(report->ref_sources, (ref_count + 2) * sizeof(char *), report->ref_sources);
        os_strdup(ref_source, report->ref_sources[ref_count]);
        os_realloc(report->references, (ref_count + 2) * sizeof(char *), report->references);
        os_strdup(url, report->references[ref_count]);
        ++ref_count;
        report->ref_sources[ref_count] = report->references[ref_count] = NULL;
    }

    wdb_finalize(stmt);

    if (SQLITE_ERROR == step_result)
        return SQLITE_ERROR;

    return SQLITE_OK;
}

int wm_vuldet_fill_report_nvd_scoring(sqlite3 *db, sqlite3_stmt *stmt, int cve_table_id, vu_report *report) {
    //Query for scoring
    if (wm_vuldet_prepare(db, vu_queries[VU_GET_SCORING], -1, &stmt, NULL) != SQLITE_OK) {
        return SQLITE_ERROR;
    }
    sqlite3_bind_int(stmt, 1, cve_table_id);

    int step_result = 0;
    while (step_result = wm_vuldet_step(stmt), (SQLITE_ROW == step_result)) {
        cv_scoring_system **cvss;
        char *vector_string = (char *)sqlite3_column_text(stmt, 0);
        double base_score = sqlite3_column_double(stmt, 1);
        double exploitability_score = sqlite3_column_double(stmt, 2);
        double impact_score = sqlite3_column_double(stmt, 3);
        char *cvss_ver = (char *)sqlite3_column_text(stmt, 4);

        if (!cvss_ver) {
            continue;
        }

        if (strstr(cvss_ver, "2.") && !report->cvss2) {
            cvss = &report->cvss2;
        } else if (strstr(cvss_ver, "3.") && !report->cvss3) {
            cvss = &report->cvss3;
        } else {
            continue;
        }

        os_calloc(1, sizeof(cv_scoring_system), *cvss);
        w_strdup(vector_string, (*cvss)->vector_string);
        (*cvss)->base_score = base_score;
        (*cvss)->exploitability_score = exploitability_score;
        (*cvss)->impact_score = impact_score;
    }

    wdb_finalize(stmt);

    if (SQLITE_ERROR == step_result)
        return SQLITE_ERROR;

    if (report->cvss3) {
        if (report->cvss3->base_score >= 9) {
            os_strdup(vu_severities[VU_CRITICAL], report->severity);
        } else if (report->cvss3->base_score >= 7) {
            os_strdup(vu_severities[VU_HIGH], report->severity);
        } else if (report->cvss3->base_score >= 4) {
            os_strdup(vu_severities[VU_MEDIUM], report->severity);
        } else if (report->cvss3->base_score >= 0.1) {
            os_strdup(vu_severities[VU_LOW], report->severity);
        } else {
            os_strdup(vu_severities[VU_NONE], report->severity);
        }
    } else if (report->cvss2) {
        if (report->cvss2->base_score >= 7) {
            os_strdup(vu_severities[VU_HIGH], report->severity);
        } else if (report->cvss2->base_score >= 4) {
            os_strdup(vu_severities[VU_MEDIUM], report->severity);
        } else {
            os_strdup(vu_severities[VU_LOW], report->severity);
        }
    }

    return SQLITE_OK;
}

int wm_vuldet_fill_report_oval_data(sqlite3 *db, sqlite3_stmt *stmt, agent_software *agents_it, cve_vuln_pkg *pkg, vu_report *report) {
    // As we consider the NVD as our source of trust, within this statement we will
    // add information only if that information was not already added by the NVD
    // report, or if the information is not repeated.
    const char *target;
    char *severity;
    char *rationale;
    char *published;
    char *updated;
    char *cvss;
    char *cvss3;
    char *cvss_vector;
    char *cvss3_vector;
    char *cwe;
    char *reference;
    char *bugzilla_reference;
    char* advisory;
    int adv_count = 0;
    int ref_count = 0;
    int bugref_count = 0;

    if (agents_it->dist == FEED_REDHAT) {
        target = "JREDHAT";
    } else {
        target = vu_feed_tag[agents_it->dist_ver];
    }

    if (pkg->vuln_cond) {
        if (!report->operation) w_strdup(pkg->vuln_cond->operation, report->operation);
        if (!report->operation_value) w_strdup(pkg->vuln_cond->operation_value, report->operation_value);
        if (!report->condition) w_strdup(pkg->vuln_cond->condition, report->condition);
    }

    // Adding OVALs information
    if (wm_vuldet_prepare(db, vu_queries[VU_GET_VULN_INFO], -1, &stmt, NULL) != SQLITE_OK) {
        return SQLITE_ERROR;
    }

    sqlite3_bind_text(stmt, 1, report->cve, -1, NULL);
    sqlite3_bind_text(stmt, 2, target, -1, NULL);

    if (SQLITE_ROW == wm_vuldet_step(stmt)) {
        severity = (char *)sqlite3_column_text(stmt, 0);
        published = (char *)sqlite3_column_text(stmt, 1);
        updated = (char *)sqlite3_column_text(stmt, 2);
        rationale = (char *)sqlite3_column_text(stmt, 3);
        cvss = (char *)sqlite3_column_text(stmt, 4);
        cvss3 = (char *)sqlite3_column_text(stmt, 5);
        cvss_vector = (char *)sqlite3_column_text(stmt, 6);
        cvss3_vector = (char *)sqlite3_column_text(stmt, 7);
        cwe = (char *)sqlite3_column_text(stmt, 8);

        if (!report->rationale) w_strdup(rationale, report->rationale);

        if (severity) {
            if (agents_it->dist == FEED_REDHAT) {
                /* For RHEL/CentOS agents we trust in the severity
                from the vendor feed instead of the NVD.
                It avoids NULL severity for some vulnerabilities */
                os_free(report->severity);
                w_strdup(severity, report->severity);
            } else if (!report->severity) {
                w_strdup(severity, report->severity);
            }
        } else if (!report->severity) {
            os_strdup(vu_severities[VU_UNKNOWN], report->severity);
        }

        if (!report->published) w_strdup(published, report->published);
        if (!report->updated && updated) w_strdup(updated, report->updated);
        if (!report->cvss2 && cvss) {
            os_calloc(1, sizeof(cv_scoring_system), report->cvss2);
            w_strdup(cvss_vector, report->cvss2->vector_string);
            report->cvss2->base_score = strtod(cvss, NULL);
        }
        if (!report->cvss3 && cvss3) {
            os_calloc(1, sizeof(cv_scoring_system), report->cvss3);
            w_strdup(cvss3_vector, report->cvss3->vector_string);
            report->cvss3->base_score = strtod(cvss3, NULL);
        }
        if (!report->cwe) w_strdup(cwe, report->cwe);

        wdb_finalize(stmt);

        // Adding references URLs
        if (wm_vuldet_prepare(db, vu_queries[VU_REFS_QUERY], -1, &stmt, NULL) != SQLITE_OK) {
            return SQLITE_ERROR;
        }

        sqlite3_bind_text(stmt, 1, report->cve, -1, NULL);
        sqlite3_bind_text(stmt, 2, target, -1, NULL);

        for (ref_count = 0; report->references && report->references[ref_count]; ++ref_count) {
            // Do nothing. Just counting the number of references just
            // in case the NVD could have add some references URLs.
        }

        while (SQLITE_ROW == wm_vuldet_step(stmt)) {
            reference = (char *)sqlite3_column_text(stmt, 0);

            // Checking if the reference is already in the list
            int skip = 0;
            for (int i = 0; report->references && report->references[i]; ++i) {
                if (!strcmp(reference, report->references[i])) {
                    skip = 1;
                    break;
                }
            }

            if (skip) continue;

            // The reference is not in the list... adding it.
            os_realloc(report->references, (ref_count + 2) * sizeof(char *), report->references);
            os_strdup(reference, report->references[ref_count]);
            report->references[++ref_count] = NULL;
        }

        wdb_finalize(stmt);

        // Adding bugzilla references URLs
        if (wm_vuldet_prepare(db, vu_queries[VU_BUG_REFS_QUERY], -1, &stmt, NULL) != SQLITE_OK) {
            return SQLITE_ERROR;
        }

        sqlite3_bind_text(stmt, 1, report->cve, -1, NULL);
        sqlite3_bind_text(stmt, 2, target, -1, NULL);

        for (bugref_count = 0; report->bugzilla_references && report->bugzilla_references[bugref_count]; ++bugref_count) {
            // Do nothing. Just counting the number of references just
            // in case the NVD could have add some bugzilla URLs.
        }

        while (SQLITE_ROW == wm_vuldet_step(stmt)) {
            bugzilla_reference = (char *)sqlite3_column_text(stmt, 0);
            os_realloc(report->bugzilla_references, (bugref_count + 2) * sizeof(char *), report->bugzilla_references);
            os_strdup(bugzilla_reference, report->bugzilla_references[bugref_count]);
            report->bugzilla_references[++bugref_count] = NULL;
        }

        wdb_finalize(stmt);

        // Adding advisories
        if (wm_vuldet_prepare(db, vu_queries[VU_ADVISORY_QUERY], -1, &stmt, NULL) != SQLITE_OK) {
            return SQLITE_ERROR;
        }

        sqlite3_bind_text(stmt, 1, report->cve, -1, NULL);
        sqlite3_bind_text(stmt, 2, target, -1, NULL);

        for (adv_count = 0; report->advisories && report->advisories[adv_count]; ++adv_count) {
            // Do nothing. Just counting the number of references just
            // in case the NVD could have add some advisories.
        }

        while (SQLITE_ROW == wm_vuldet_step(stmt)) {
            advisory = (char *)sqlite3_column_text(stmt, 0);
            os_realloc(report->advisories, (adv_count + 2) * sizeof(char *), report->advisories);
            os_strdup(advisory, report->advisories[adv_count]);
            report->advisories[++adv_count] = NULL;
        }

        wdb_finalize(stmt);
    }

    wdb_finalize(stmt);

    return SQLITE_OK;
}

int wm_vuldet_send_agent_report(sqlite3 *db, OSHash *cve_table, agent_software *agents_it) {
    OSHashNode *hash_node;
    unsigned int inode_it = 0;
    sqlite3_stmt *stmt = NULL;
    vu_report *report = NULL;
    time_t start_time;
    int vuln_reported = 0;
    int vuln_reported_nvd = 0;
    int vuln_reported_vendor = 0;
    int cve_insert_result = OS_SUCCESS;

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_START_VUL_AG_SEND, atoi(agents_it->agent_id));

    start_time = time(NULL);

    int sock = wm_vuldet_get_wdb_socket();

    hash_node = OSHash_Begin(cve_table, &inode_it);

    while(hash_node) {
        char *cve;
        cve_vuln_pkg *pkg  = NULL;
        cve_vuln_pkg *next = NULL;

        cve = hash_node->key;
        pkg = hash_node->data;

        do {
            next = pkg->next;

            if (pkg->discard) {
                pkg = next;
                continue;
            }

            os_calloc(1, sizeof(vu_report), report);

            // Adding agent information
            w_strdup(agents_it->agent_id, report->agent_id);
            w_strdup(agents_it->agent_name, report->agent_name);
            w_strdup(agents_it->agent_ip, report->agent_ip);

            // Adding package information
            w_strdup(cve, report->cve);
            w_strdup(pkg->bin_name, report->software);
            w_strdup(pkg->src_name, report->source);
            if (!strncmp("-1:", pkg->version, 3)) {
                w_strdup((pkg->version + 3), report->version);
            } else {
                w_strdup(pkg->version, report->version);
            }
            w_strdup(pkg->arch, report->arch);

            // Build title
            os_calloc(OS_SIZE_512, sizeof(char), report->title);
            snprintf(report->title, OS_SIZE_512, "%s affects %s", report->cve, report->software);

            // Adding data from NVD
            if ((pkg->feed == VU_SRC_NVD) && pkg->nvd_cond) {
                wm_vuldet_build_nvd_report_condition(pkg->nvd_cond, report);
            }

            int cve_table_id = 0;

            if (wm_vuldet_fill_report_nvd_cve_info(db, stmt, report, &cve_table_id) != SQLITE_OK) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_REPORT_NVD_INFO_ERROR, atoi(agents_it->agent_id));
                goto error;
            }

            if (wm_vuldet_fill_report_nvd_references(db, stmt, cve_table_id, report) != SQLITE_OK) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_REPORT_NVD_REF_ERROR, atoi(agents_it->agent_id));
                goto error;
            }

            if (wm_vuldet_fill_report_nvd_scoring(db, stmt, cve_table_id, report) != SQLITE_OK) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_REPORT_NVD_SCORE_ERROR, atoi(agents_it->agent_id));
                goto error;
            }
            if (agents_it->dist != FEED_MAC) {
                // Adding data from OVAL
                if (wm_vuldet_fill_report_oval_data(db, stmt, agents_it, pkg, report) != SQLITE_OK) {
                    mterror(WM_VULNDETECTOR_LOGTAG, VU_REPORT_OVAL_ERROR, atoi(agents_it->agent_id));
                    goto error;
                }
            }

            //Save the vulnerability in the agent database
            if (OS_INVALID == wdb_agents_vuln_cve_insert(atoi(agents_it->agent_id), report->software, report->version, report->arch, report->cve, &sock)) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, "Failed to insert %s for package %s in the agent %.3d database",
                         report->cve ? report->cve : "null", report->software ? report->software : "null", atoi(agents_it->agent_id));
                cve_insert_result = OS_INVALID;
            }

            // Sending CVE report
            if (wm_vuldet_send_cve_report(report)) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_SEND_AGENT_REPORT_ERROR, report->cve, report->software, atoi(agents_it->agent_id));
            } else {
                if (pkg->feed & VU_SRC_NVD) {
                    vuln_reported_nvd++;
                }
                if (pkg->feed & VU_SRC_OVAL) {
                    vuln_reported_vendor++;
                }
                vuln_reported++;
            }
            wm_vuldet_free_report(report);

            pkg = next;
        } while (pkg);

        hash_node = OSHash_Next(cve_table, &inode_it, hash_node);
    }

    wdb_finalize(stmt);

    if (OS_INVALID == cve_insert_result) {
        mtwarn(WM_VULNDETECTOR_LOGTAG, "Failed to insert vulnerabilities in the agent %.3d database", atoi(agents_it->agent_id));
    }

    if (agents_it->dist != FEED_MAC) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_VULN_SEND_AG_FEED, vuln_reported_nvd, atoi(agents_it->agent_id), "NVD");
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_VULN_SEND_AG_FEED, vuln_reported_vendor, atoi(agents_it->agent_id), "vendor");
    }
    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_VULN_SEND_AG, vuln_reported, atoi(agents_it->agent_id));
    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FUNCTION_TIME, time(NULL) - start_time, "report", atoi(agents_it->agent_id));

    return 0;

error:

    wm_vuldet_free_report(report);
    return wm_vuldet_sql_error(db, stmt);
}

int wm_vuldet_send_cve_report(vu_report *report) {
    cJSON *alert = NULL;
    cJSON *alert_cve = NULL;
    int retval = OS_INVALID;
    int send_queue;
    char *str_json = NULL;
    char header[OS_SIZE_256 + 1];
    char alert_msg[OS_MAXSTR + 1];
    // Define time to sleep between messages sent
    int usec = 1000000 / wm_max_eps;
    char *timestamp = NULL;

    if (alert = cJSON_CreateObject(), !alert) {
        return retval;
    }

    if (alert_cve = cJSON_CreateObject(), alert_cve) {
        cJSON *j_package = NULL;
        cJSON *j_cvss = NULL;

        cJSON_AddItemToObject(alert, "vulnerability", alert_cve);

        if (j_package = cJSON_CreateObject(), !j_package) {
            goto end;
        }
        cJSON_AddItemToObject(alert_cve, "package", j_package);

        cJSON_AddStringToObject(j_package, "name", report->software);
        if (report->source) cJSON_AddStringToObject(j_package, "source", report->source);
        if (report->version && *report->version) cJSON_AddStringToObject(j_package, "version", report->version);
        if (report->generated_cpe) cJSON_AddStringToObject(j_package, "generated_cpe", report->generated_cpe);
        if (report->arch && *report->arch) cJSON_AddStringToObject(j_package, "architecture", report->arch);

        if (report->condition && *report->condition != '\0') {
            cJSON_AddStringToObject(j_package, "condition", report->condition);
        } else if (report->operation && report->operation_value) {
            os_free(report->condition);
            os_calloc(OS_SIZE_1024 + 1, sizeof(char), report->condition);
            snprintf(report->condition, OS_SIZE_1024, "Package %s %s", report->operation, report->operation_value);
            cJSON_AddStringToObject(j_package, "condition", report->condition);
        }

        if (report->cvss2 || report->cvss3) {
            cJSON *j_cvss_node;
            int i;

            if (j_cvss = cJSON_CreateObject(), !j_cvss) {
                goto end;
            }
            cJSON_AddItemToObject(alert_cve, "cvss", j_cvss);

            for (i = 0; i < 2; i++) {
                cv_scoring_system *cvss = !i ? report->cvss2 : report->cvss3;
                if (cvss) {
                    if (j_cvss_node = cJSON_CreateObject(), !j_cvss_node) {
                        goto end;
                    }
                    cJSON_AddItemToObject(j_cvss, !i ? "cvss2" : "cvss3", j_cvss_node);
                    // Although the function wm_vuldet_get_cvss could return NULL, we are not going to end in that case
                    // because some feeds, like Ubuntu or Debian, do not provide this information.
                    if (cvss->vector_string) cJSON_AddItemToObject(j_cvss_node, "vector", wm_vuldet_get_cvss(cvss->vector_string));
                    if (cvss->base_score) cJSON_AddItemToObject(j_cvss_node, "base_score", cJSON_CreateNumber(cvss->base_score));
                    if (cvss->exploitability_score) cJSON_AddItemToObject(j_cvss_node, "exploitability_score", cJSON_CreateNumber(cvss->exploitability_score));
                    if (cvss->impact_score) cJSON_AddItemToObject(j_cvss_node, "impact_score", cJSON_CreateNumber(cvss->impact_score));
                }
            }
        }

        // Set the alert body
        cJSON_AddStringToObject(alert_cve, "cve", report->cve);
        if (report->title) {
            cJSON_AddStringToObject(alert_cve, "title", report->title);
        }
        if (report->rationale) {
            cJSON_AddStringToObject(alert_cve, "rationale", report->rationale);
        }
        cJSON_AddStringToObject(alert_cve, "severity", wm_vuldet_get_unified_severity(report->severity));
        if (timestamp = wm_vuldet_normalize_date(&report->published), timestamp) {
            cJSON_AddStringToObject(alert_cve, "published", timestamp);
        }
        if (timestamp = wm_vuldet_normalize_date(&report->updated), timestamp) {
            cJSON_AddStringToObject(alert_cve, "updated", timestamp);
        }
        if (report->cwe) cJSON_AddStringToObject(alert_cve, "cwe_reference", report->cwe);
        if (report->advisories) {
            cJSON *j_advisories = NULL;
            if (j_advisories = cJSON_CreateArray(), !j_advisories) {
                goto end;
            }
            int advcount;
            for (advcount = 0; report->advisories[advcount]; ++advcount)
            {
                cJSON *j_adv_item = cJSON_CreateString(report->advisories[advcount]);
                cJSON_AddItemToArray(j_advisories, j_adv_item);
            }
            cJSON_AddItemToObject(alert_cve, "advisories_ids", j_advisories);
        }
        if (report->bugzilla_references) {
            cJSON *j_bug_references = NULL;
            if (j_bug_references = cJSON_CreateArray(), !j_bug_references) {
                goto end;
            }
            int refcount;
            for (refcount = 0; report->bugzilla_references[refcount]; ++refcount)
            {
                cJSON *j_bug_item = cJSON_CreateString(report->bugzilla_references[refcount]);
                cJSON_AddItemToArray(j_bug_references, j_bug_item);
            }
            cJSON_AddItemToObject(alert_cve, "bugzilla_references", j_bug_references);
        }
        if (report->references) {
            cJSON *j_cvs_references = NULL;
            if (j_cvs_references = cJSON_CreateArray(), !j_cvs_references) {
                goto end;
            }
            int refcount;
            for (refcount = 0; report->references[refcount]; ++refcount)
            {
                cJSON *j_ref_item = cJSON_CreateString(report->references[refcount]);
                cJSON_AddItemToArray(j_cvs_references, j_ref_item);
            }
            cJSON_AddItemToObject(alert_cve, "references", j_cvs_references);
        }
        if (report->assigner) cJSON_AddStringToObject(alert_cve, "assigner", report->assigner);
        if (report->cve_version) cJSON_AddStringToObject(alert_cve, "cve_version", report->cve_version);
    } else {
        goto end;
    }

    str_json = cJSON_PrintUnformatted(alert);

    // Send an alert as a manager if there is no IP assigned
    if (report->agent_ip) {
        snprintf(header, OS_SIZE_256, VU_ALERT_HEADER, atoi(report->agent_id), report->agent_name, report->agent_ip);
        snprintf(alert_msg, OS_MAXSTR, VU_ALERT_JSON, str_json);
        send_queue = SECURE_MQ;
    } else {
        snprintf(header, OS_SIZE_256, "%s", VU_WM_NAME);
        snprintf(alert_msg, OS_MAXSTR, "%s", str_json);
        send_queue = LOCALFILE_MQ;
    }

    if (report->is_hotfix) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_HOTFIX_VUL,
            atoi(report->agent_id),
            report->cve,
            report->condition ? report->condition : "Hotfix is not installed.");
    } else {
        if (report->software && report->version && report->agent_id && report->cve) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACK_VER_VULN, report->software,
            report->version, atoi(report->agent_id), report->cve,
            report->condition && *report->condition != '\0' ? report->condition :
            "exists");
        }
    }

    if (wm_sendmsg(usec, *vu_queue, alert_msg, header, send_queue) < 0) {
        mterror(WM_VULNDETECTOR_LOGTAG, QUEUE_ERROR, DEFAULTQUEUE, strerror(errno));
        if ((*vu_queue = StartMQ(DEFAULTQUEUE, WRITE, INFINITE_OPENQ_ATTEMPTS)) < 0) {
            mterror_exit(WM_VULNDETECTOR_LOGTAG, QUEUE_FATAL, DEFAULTQUEUE);
        }
    }

    retval = 0;
end:
    os_free(str_json);
    cJSON_Delete(alert);
    return retval;
}

int wm_vuldet_linux_rm_nvd_not_affected_packages(sqlite3 *db, agent_software *agent, const char *cve, cve_vuln_pkg *first, int *vuln_discarded) {
    sqlite3_stmt *stmt = NULL;
    cve_vuln_pkg *pkg = first;
    cve_vuln_pkg *next = NULL;
    int vendor_it;
    int vendor_len;

    do {
        next = pkg->next;

        if ((pkg->feed == VU_SRC_NVD) && !pkg->discard && strcmp(pkg->bin_name, PR_KERNEL) && strcmp(pkg->bin_name, PR_UBUNTU) && strcmp(pkg->bin_name, PR_DEBIAN) && strcmp(pkg->bin_name, PR_REDHAT) && strcmp(pkg->bin_name, PR_ARCH)) {

            // If it is possible to identify that the package belongs to the lineage
            // of any vendor, it means that it is not affected and we can discard it
            if (pkg->vendor) {
                if (agent->dist == FEED_ARCH) {
                    vendor_len = array_size(vu_vendor_list_arch);
                    for(vendor_it = 0; vendor_it < vendor_len; ++vendor_it) {
                        if(!strcmp(pkg->vendor, vu_vendor_list_arch[vendor_it])) {
                            // This is an Arch package, so the OVAL said that it is not affected
                            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_AFF, pkg->bin_name, cve, "OVAL");
                            pkg->discard = 1;
                            (*vuln_discarded)++;
                            break;
                        }
                    }
                } else if (agent->dist == FEED_REDHAT) {
                    vendor_len = array_size(vu_vendor_list_redhat);
                    for(vendor_it = 0; vendor_it < vendor_len; ++vendor_it) {
                        if(!strcmp(pkg->vendor, vu_vendor_list_redhat[vendor_it])) {
                            // This is a RedHat package, so the OVAL said that it is not affected
                            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_AFF, pkg->bin_name, cve, "OVAL");
                            pkg->discard = 1;
                            (*vuln_discarded)++;
                            break;
                        }
                    }
                } else {
                    vendor_len = array_size(vu_vendor_list_ubuntu_debian);
                    for(vendor_it = 0; vendor_it < vendor_len; ++vendor_it) {
                        if(strcasestr(pkg->vendor, vu_vendor_list_ubuntu_debian[vendor_it])) {
                            // This is an Ubuntu/Debian package, so the OVAL said that it is not affected
                            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_AFF, pkg->bin_name, cve, "OVAL");
                            pkg->discard = 1;
                            (*vuln_discarded)++;
                            break;
                        }
                    }
                }
            }

            // If the package couldn't be identified by vendor, we check the OVAL
            // information to see if it is not affected and we can discard it
            if (!pkg->discard) {
                if (wm_vuldet_prepare(db, vu_queries[VU_GET_VULN_COUNT], -1, &stmt, NULL) != SQLITE_OK) {
                    mterror(WM_VULNDETECTOR_LOGTAG, VU_FILTER_VULN_ERROR, cve, pkg->bin_name);
                    return wm_vuldet_sql_error(db, stmt);
                }

                sqlite3_bind_text(stmt, 1, cve, -1, NULL);
                sqlite3_bind_text(stmt, 2, vu_feed_tag[agent->dist_ver], -1, NULL);

                if ((SQLITE_ROW == wm_vuldet_step(stmt)) && sqlite3_column_int(stmt, 0)) {
                    // We can discard NVD vulnerabilities in two cases:
                    // - The OVAL said that this package is patched or unaffected.
                    // - The OVAL said that this binary is not affected.
                    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_AFF, pkg->bin_name, cve, "OVAL");
                    pkg->discard = 1;
                    (*vuln_discarded)++;
                }

                wdb_finalize(stmt);
            }
        }

        pkg = next;
    } while (pkg);

    wdb_finalize(stmt);

    return 0;
}

void wm_vuldet_linux_rm_nvd_not_dependencies_met_packages(const char *cve, cve_vuln_pkg *first, int *vuln_discarded) {
    cve_vuln_pkg *pkg = first;
    cve_vuln_pkg *next = NULL;

    do {
        next = pkg->next;

        if ((pkg->feed & VU_SRC_NVD) && !pkg->discard && pkg->nvd_cond) {

            if (pkg->nvd_cond->parent == 0 && !strcmp(pkg->nvd_cond->operator, "AND")) { // Analyze children
                int child = 0;

                for (child = 0; child < MAX_RELATED_PKGS && pkg->nvd_cond->children[child]; child++) {
                    int found = 0;
                    cve_vuln_pkg *tmp = first;

                    while (tmp) {
                        if ((tmp->feed & VU_SRC_NVD) && !tmp->discard && tmp->nvd_cond && (pkg->nvd_cond->children[child] == tmp->nvd_cond->id)) {
                            found = 1;
                            break;
                        }
                        tmp = tmp->next;
                    }

                    if (!found) {
                        // If any dependency isn't installed then the package is not vulnerable
                        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NO_DEP, pkg->bin_name, cve, "children", pkg->nvd_cond->children[child]);
                        pkg->discard = 1;
                        (*vuln_discarded)++;
                        break;
                    }
                }
            }

            else if (pkg->nvd_cond->parent != 0 && !strcmp(pkg->nvd_cond->operator, "OR")) { // Analyze siblings
                int sibling = 0;

                for (sibling = 0; sibling < MAX_RELATED_PKGS && pkg->nvd_cond->siblings[sibling]; sibling++) {
                    int found = 0;
                    cve_vuln_pkg *tmp = first;

                    while (tmp) {
                        if ((tmp->feed & VU_SRC_NVD) && !tmp->discard && tmp->nvd_cond && (pkg->nvd_cond->siblings[sibling] == tmp->nvd_cond->conf_id)) {
                            found = 1;
                            break;
                        }
                        tmp = tmp->next;
                    }

                    if (!found) {
                        // If any dependency isn't installed then the package is not vulnerable
                        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NO_DEP, pkg->bin_name, cve, "sibling", pkg->nvd_cond->siblings[sibling]);
                        pkg->discard = 1;
                        (*vuln_discarded)++;
                        break;
                    }
                }
            }
        }

        pkg = next;
    } while (pkg);

    return;
}

void wm_vuldet_linux_rm_nvd_not_vulnerable_packages(const char *cve, cve_vuln_pkg *first, int *vuln_discarded) {
    cve_vuln_pkg *pkg = first;
    cve_vuln_pkg *next = NULL;

    do {
        next = pkg->next;

        if ((pkg->feed & VU_SRC_NVD) && !pkg->discard && pkg->nvd_cond && !pkg->nvd_cond->vulnerable) {
            // Some packages are dependencies of others but aren't vulnerable, so we won't alert those packages
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_DEP_NOT_VU, pkg->bin_name, cve);
            pkg->discard = 1;
            (*vuln_discarded)++;
        }

        pkg = next;
    } while (pkg);

    return;
}

int wm_vuldet_linux_rm_oval_not_affected_packages(sqlite3 *db, const char *cve, cve_vuln_pkg *first, int *vuln_discarded) {
    sqlite3_stmt *stmt = NULL;
    cve_vuln_pkg *pkg = first;
    cve_vuln_pkg *next = NULL;

    do {
        next = pkg->next;

        if ((pkg->feed == VU_SRC_OVAL) && !pkg->discard && pkg->vuln_cond && !strcmp(pkg->vuln_cond->state, "Unfixed")) {

            if (wm_vuldet_prepare(db, vu_queries[VU_GET_NVD_CVE_COUNT], -1, &stmt, NULL) != SQLITE_OK) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_FILTER_VULN_NVD_ERROR, cve);
                return wm_vuldet_sql_error(db, stmt);
            }

            sqlite3_bind_text(stmt, 1, cve, -1, NULL);

            // If this row returns non-zero, it means that this CVE has entries in
            // the NVD, so we need to evaluate if we have to discard it or not
            if ((SQLITE_ROW == wm_vuldet_step(stmt)) && sqlite3_column_int(stmt, 0)) {

                wdb_finalize(stmt);

                if (wm_vuldet_prepare(db, vu_queries[VU_GET_NVD_MATCHES_COUNT], -1, &stmt, NULL) != SQLITE_OK) {
                    mterror(WM_VULNDETECTOR_LOGTAG, VU_FILTER_VULN_ERROR, cve, pkg->bin_name);
                    return wm_vuldet_sql_error(db, stmt);
                }

                sqlite3_bind_text(stmt, 1, cve, -1, NULL);
                sqlite3_bind_text(stmt, 2, pkg->bin_name, -1, NULL);

                if (pkg->src_name) {
                    sqlite3_bind_text(stmt, 3, pkg->src_name, -1, NULL);
                } else {
                    sqlite3_bind_text(stmt, 3, pkg->bin_name, -1, NULL);
                }

                if ((SQLITE_ROW == wm_vuldet_step(stmt)) && !sqlite3_column_int(stmt, 0)) {
                    // We can discard this vulnerability if the NVD said that this version is not affected
                    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_AFF, pkg->bin_name, cve, "NVD");
                    pkg->discard = 1;
                    (*vuln_discarded)++;
                }

                if (pkg->discard) {
                    cve_vuln_pkg *tmp = first;
                    bool discard = false;

                    while (tmp) {
                        if ((tmp->feed == VU_SRC_OVAL) && !tmp->discard) {

                            if (pkg->src_name && tmp->src_name && !strcmp(pkg->src_name, tmp->src_name)) {
                                discard = true;
                            } else if (pkg->src_name && !strcmp(pkg->src_name, tmp->bin_name)) {
                                discard = true;
                            } else if (tmp->src_name && !strcmp(pkg->bin_name, tmp->src_name)) {
                                discard = true;
                            }

                            if (discard) {
                                // We will discard all the packages with the same source
                                mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_AFF, tmp->bin_name, cve, "NVD");
                                tmp->discard = 1;
                                (*vuln_discarded)++;
                                discard = false;
                            }
                        }
                        tmp = tmp->next;
                    }
                }
            }

            wdb_finalize(stmt);
        }

        pkg = next;
    } while (pkg);

    wdb_finalize(stmt);

    return 0;
}

int wm_vuldet_linux_rm_false_positivies(sqlite3 *db, agent_software *agent, OSHash *cve_table) {
    OSHashNode *hash_node;
    unsigned int inode_it = 0;
    time_t start_time;
    int *vuln_discarded;

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_START_VUL_AG_FIL, atoi(agent->agent_id));

    start_time = time(NULL);

    hash_node = OSHash_Begin(cve_table, &inode_it);

    os_calloc(1, sizeof(int), vuln_discarded);

    while(hash_node) {
        char *cve;
        cve_vuln_pkg *first = NULL;

        cve = hash_node->key;
        first = hash_node->data;

        if (agent->dist != FEED_MAC) {
            // We will discard the packages unaffected or already fixed by the vendor (Ubuntu/Debian/RedHat)
            if (wm_vuldet_linux_rm_nvd_not_affected_packages(db, agent, cve, first, vuln_discarded)) {
                os_free(vuln_discarded);
                return OS_INVALID;
            }
        }

        // We will discard the packages that depend on packages not installed
        wm_vuldet_linux_rm_nvd_not_dependencies_met_packages(cve, first, vuln_discarded);

        // We will discard the packages that have vulnerable field set to 0
        wm_vuldet_linux_rm_nvd_not_vulnerable_packages(cve, first, vuln_discarded);

        if (agent->dist != FEED_MAC) {
            // We will discard the packages not fixed by the OVAL that the NVD said they are not affected
            if (wm_vuldet_linux_rm_oval_not_affected_packages(db, cve, first, vuln_discarded)) {
                os_free(vuln_discarded);
                return OS_INVALID;
            }
        }

        hash_node = OSHash_Next(cve_table, &inode_it, hash_node);
    }

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_VULN_FILTER_AG, *vuln_discarded, atoi(agent->agent_id));
    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FUNCTION_TIME, time(NULL) - start_time, "filter", atoi(agent->agent_id));

    os_free(vuln_discarded);

    return 0;
}

int wm_vuldet_get_cve_year(const char *cve) {
    char *str_cve_year = NULL;
    int year = 0;

    // The CVEs follow the syntax: CVE-YEAR-XXXX
    // so we need just four characters, starting from the fourth position.
    os_calloc(5, sizeof(char), str_cve_year);
    memcpy(str_cve_year, cve+4, 4);

    year = atoi(str_cve_year);
    os_free(str_cve_year);

    return year;
}

int wm_vuldet_oval_discard_mismatching_cve_entries(sqlite3 *db, agent_software *agents_it, char *cve_id, char *pkg_version, char *pkg_name, version_type vertype) {
    sqlite3_stmt    *stmt = NULL;
    char            *operation;
    char            *operation_value;
    const char      *query;
    int             result;
    int             ret = 0;

    if (agents_it->dist == FEED_UBUNTU) {
        query = vu_queries[VU_GET_MULTIPLE_CVE_ENTRIES_UBUNTU];
    } else {
        query = vu_queries[VU_GET_MULTIPLE_CVE_ENTRIES];
    }

    if (wm_vuldet_prepare(db, query, -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, vu_feed_tag[agents_it->dist_ver], -1, NULL);
    sqlite3_bind_text(stmt, 2, cve_id, -1, NULL);
    sqlite3_bind_text(stmt, 3, pkg_name, -1, NULL);
    sqlite3_bind_text(stmt, 4, pkg_version, -1, NULL);

    while (result = wm_vuldet_step(stmt), result == SQLITE_ROW) {
        operation = (char *)sqlite3_column_text(stmt, 0);
        operation_value = (char *)sqlite3_column_text(stmt, 1);
        if (ret = wm_checks_package_vulnerability(pkg_version, operation, operation_value, vertype), ret == VU_NOT_VULNERABLE) {
            break;
        }
    }

    if (result != SQLITE_DONE && result != SQLITE_ROW) {
        return wm_vuldet_sql_error(db, stmt);
    }

    wdb_finalize(stmt);
    return ret;
}

int wm_vuldet_linux_oval_vulnerabilities(sqlite3 *db, agent_software *agents_it, OSHash *cve_table) {
    sqlite3_stmt *stmt = NULL;
    const char *query;
    version_type vertype = VER_TYPE_NONE;
    cve_vuln_pkg *vuln_pkg = NULL;
    time_t start_time;
    int vuln_count = 0;
    int nvd_year = 0;
    char * nvd_year_str = NULL;

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_START_OVAL_AG_AN, atoi(agents_it->agent_id));

    start_time = time(NULL);

    // Getting the configured NVD year
    if (wm_vuldet_prepare(db, vu_queries[VU_GET_NVD_CONFIGURED_YEAR], -1, &stmt, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_GET_NVD_YEAR_ERROR);
        return wm_vuldet_sql_error(db, stmt);
    }

    if (SQLITE_ROW != wm_vuldet_step(stmt)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_GET_NVD_YEAR_ERROR);
        wdb_finalize(stmt);
        return OS_INVALID;
    }

    nvd_year_str = (char *)sqlite3_column_text(stmt, 0);
    if (nvd_year_str) {
        nvd_year = atoi(nvd_year_str);
    }

    wdb_finalize(stmt);

    // Getting CVEs from database
    if (agents_it->dist == FEED_REDHAT) {
        query = vu_queries[VU_JOIN_RH_QUERY];
        if (agents_it->flags.centos) {
            vertype = VER_TYPE_RPM_CENTOS;
        } else {
            vertype = VER_TYPE_RPM;
        }
    } else if (agents_it->dist == FEED_DEBIAN) {
        query = vu_queries[VU_JOIN_DEBIAN_QUERY];
        vertype = VER_TYPE_DEB;
    } else if (agents_it->dist == FEED_ARCH) {
        query = vu_queries[VU_JOIN_ARCH_QUERY];
        vertype = VER_TYPE_DEB; // Arch version format is compatible with the Deb comparator
    } else {
        query = vu_queries[VU_JOIN_QUERY];
        vertype = VER_TYPE_DEB;
    }

    if (wm_vuldet_prepare(db, query, -1, &stmt, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_GET_PACKAGES_ERROR, atoi(agents_it->agent_id));
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, vu_feed_tag[agents_it->dist_ver], -1, NULL);
    sqlite3_bind_int(stmt, 2, strtol(agents_it->agent_id, NULL, 10));

    int result = wm_vuldet_step(stmt);
    switch (result) {
    case SQLITE_ROW:
        break;
    case SQLITE_DONE:
        // No vulnerabilities could be found after scanning the OVAL for this agent
        // Leave the function since the query returned no results
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_OVAL_VULN_NOT_FOUND, atoi(agents_it->agent_id));
        wdb_finalize(stmt);
        return 0;
    default:
        return wm_vuldet_sql_error(db, stmt);
    }

    do {
        char condition[OS_SIZE_1024 + 1];
        char state[50];
        char *cve;
        char *package;
        char *source;
        char *version;
        char *src_version;
        char *arch;
        char *vendor;
        char *operation;
        char *operation_value;
        int v_type;

        cve = (char *)sqlite3_column_text(stmt, 0);
        package = (char *)sqlite3_column_text(stmt, 1);
        source = (char *)sqlite3_column_text(stmt, 2);
        version = (char *)sqlite3_column_text(stmt, 3);
        arch = (char *)sqlite3_column_text(stmt, 4);
        operation = (char *)sqlite3_column_text(stmt, 5);
        operation_value = (char *)sqlite3_column_text(stmt, 6);
        src_version = (char *)sqlite3_column_text(stmt, 7);
        vendor = (char *)sqlite3_column_text(stmt, 8);

        if (!cve || !package || !version || !arch) {
            continue;
        }

        // Considering that we correlate the vendor CVEs with the NVD CVEs,
        // we will take as a reference the configured year for the NVD.
        if (nvd_year > wm_vuldet_get_cve_year(cve)) continue;

        // If we have a source version, use it to find vulnerabilities.
        if (source && src_version) {
            version = src_version;
        }

        // We discard the package if its vendor is a external vendor.
        if (agents_it->dist == FEED_REDHAT && vendor && wm_vuldet_compare_vendors(vendor)) {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_TP_SOURCE, package, vendor, atoi(agents_it->agent_id));
            continue;
        }

        *condition = '\0';
        *state = '\0';
        if (v_type = wm_checks_package_vulnerability(version, operation, operation_value, vertype), v_type == OS_INVALID) {
            wdb_finalize(stmt);
            return OS_INVALID;
        }
        if (v_type == VU_NOT_FIXED) {
            snprintf(state, 15, "Unfixed");
            snprintf(condition, OS_SIZE_32, "Package unfixed");
            operation = "Unfixed";
            operation_value = "";
        } else if (v_type == VU_NOT_VULNERABLE) {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_VULN, package, cve, version, operation, operation_value, "OVAL");
            continue;
        } else if (v_type == VU_ERROR_CMP) {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_ERROR_CMP_VER, operation, version, operation_value, package);
            continue;
        } else {
            // Discard false positives when the vulnerability information differs in CVEs with several entries
            int discard_return = wm_vuldet_oval_discard_mismatching_cve_entries(db, agents_it, cve, version, package, vertype);
            if (discard_return == VU_NOT_VULNERABLE) {
                mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_DISCARD_CVE_ENTRY, package, cve, operation, operation_value);
                continue;
            } else if (discard_return == OS_INVALID) {
                wdb_finalize(stmt);
                return OS_INVALID;
            }
            snprintf(state, 15, "Fixed");
        }

        /* Add vulnerable package to CVE hash table */

        os_calloc(1, sizeof(cve_vuln_pkg), vuln_pkg);

        os_strdup(package, vuln_pkg->bin_name);
        w_strdup(source, vuln_pkg->src_name);
        os_strdup(version, vuln_pkg->version);
        os_strdup(arch, vuln_pkg->arch);
        w_strdup(vendor, vuln_pkg->vendor);

        os_calloc(1, sizeof(cve_vuln_cond), vuln_pkg->vuln_cond);

        os_strdup(state, vuln_pkg->vuln_cond->state);
        w_strdup(operation, vuln_pkg->vuln_cond->operation);
        w_strdup(operation_value, vuln_pkg->vuln_cond->operation_value);
        if (*condition) {
            os_strdup(condition, vuln_pkg->vuln_cond->condition);
        }

        vuln_pkg->feed |= VU_SRC_OVAL;
        vuln_pkg->discard = 0;
        vuln_pkg->next = NULL;

        int result = wm_vuldet_add_cve_node(vuln_pkg, cve, cve_table);

        switch (result) {
        case -1:
            mterror(WM_VULNDETECTOR_LOGTAG, VU_INSERT_PACKAGE_ERROR, package, cve, version, operation, operation_value, "OVAL");
            wm_vuldet_free_cve_node(vuln_pkg);
            break;
        case 1:
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_DUPLICATED_PACKAGE, package, cve, version, operation, operation_value, "OVAL");
            vuln_count++;
            wm_vuldet_free_cve_node(vuln_pkg);
            break;
        case 0:
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_INSERT, package, cve, version, operation, operation_value, "OVAL");
            vuln_count++;
            break;
        }

    } while (SQLITE_ROW == wm_vuldet_step(stmt));

    wdb_finalize(stmt);

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_VULN_OVAL_AG_COUNT, vuln_count, atoi(agents_it->agent_id));
    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FUNCTION_TIME, time(NULL) - start_time, "find OVAL", atoi(agents_it->agent_id));

    return 0;
}

int wm_vuldet_report_agent_vulnerabilities(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags) {

    int retval = OS_INVALID;
    OSHash *cve_table = NULL;

    if (!agent->info) {
        retval = 0;   // Scan next agent
        goto end;
    }

    if (agent->dist == FEED_WIN) {

        if (wm_vuldet_win_nvd_vulnerabilities(db, agent, flags)) {
            goto end;
        }
        retval = 0;

    } else {

        // To add a new node, call wm_vuldet_add_cve_node().
        // @cve_table stores cve_vuln_pkg type nodes.
        cve_table = OSHash_Create();
        if (!cve_table) {
            merror(LIST_ERROR);
            goto end;
        }
        if (!OSHash_setSize(cve_table, VU_CVE_TABLE_SIZE)) {
            merror(LIST_ERROR);
            goto end;
        }
        if (agent->dist != FEED_MAC) {
            if (wm_vuldet_linux_oval_vulnerabilities(db, agent, cve_table)) {
                goto end;
            }
        }
        if (wm_vuldet_linux_nvd_vulnerabilities(db, agent, cve_table)) {
            goto end;
        }
        if (wm_vuldet_linux_rm_false_positivies(db, agent, cve_table)) {
            goto end;
        }
        if (wm_vuldet_send_agent_report(db, cve_table, agent)) {
            goto end;
        }
        retval = 0;
    }

end:
    if (cve_table) {
        OSHash_Clean(cve_table, wm_vuldet_free_cve_node); // Free the CVE table
    }

    return retval;
}

int wm_vuldet_check_agent_vulnerabilities(agent_software *agents, wm_vuldet_flags *flags, time_t ignore_time) {
    agent_software *agents_it;
    sqlite3 *db;
    sqlite3_stmt *stmt = NULL;
    int result;
    int i;

    if (!agents) {
        mtinfo(WM_VULNDETECTOR_LOGTAG, VU_AG_NO_TARGET);
        return 0;
    } else if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CVEDB_ERROR);
        return wm_vuldet_sql_error(db, stmt);
    }

    // Iterate agents to look for vulnerabilities
    for (i = 0, agents_it = agents;; i++) {

        mtinfo(WM_VULNDETECTOR_LOGTAG, VU_START_AG_AN, atoi(agents_it->agent_id));
        time_t start = time(NULL);

        // Check there is available vulnerabilities for this agent
        if (agents_it->dist != FEED_WIN && agents_it->dist != FEED_MAC) {
            result = wm_vuldet_db_empty(db, agents_it->dist_ver);
            if (result == 0) {
                // There is no data in the VULNERABILITIES table for this agent
                // It has to be skipped instead of being scanned against the NVD to avoid false positives
                mtwarn(WM_VULNDETECTOR_LOGTAG, VU_OVAL_UNAVAILABLE_DATA, atoi(agents_it->agent_id));
                if (agents_it->next) {
                    agents_it = agents_it->next;
                    continue;
                } else {
                    break;
                }
            } else if (result == OS_INVALID) {
                // DB error
                break;
            }
        }

        // Reset the tables before scanning each agent
        wm_vuldet_reset_tables(db);

        int request = 0;
        // First step: collect its software
        if (result = wm_vuldet_get_software_info(agents_it, db, ignore_time, &request), result == OS_INVALID) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_GET_SOFTWARE_ERROR, atoi(agents_it->agent_id));
            break;
        }

        // result == 2 skips the agent
        // is used when no hotfixes are available or no packages have been marked for scanning
        if (result != 2) {
            if (VU_SOFTWARE_FULL_REQ == request) {
                //Clean the vulnerabilities in the agent database
                int sock = wm_vuldet_get_wdb_socket();
                if (OS_INVALID == wdb_agents_vuln_cve_clear(atoi(agents_it->agent_id), &sock)) {
                    mtwarn(WM_VULNDETECTOR_LOGTAG, "Failed to clear vulnerabilities from the agent %s database", agents_it->agent_id);
                }
            }
            // Second step: find and report vulnerabilities
            if (wm_vuldet_report_agent_vulnerabilities(db, agents_it, flags) < 0) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_REPORT_ERROR, atoi(agents_it->agent_id), sqlite3_errmsg(db));
                break;
            }
        }

        mtinfo(WM_VULNDETECTOR_LOGTAG, VU_AGENT_FINISH, atoi(agents_it->agent_id));
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FUNCTION_TIME, time(NULL) - start, "scan", atoi(agents_it->agent_id));

        if (agents_it->next) {
            agents_it = agents_it->next;
        } else {
            break;
        }
    }

    // Reset the tables
    wm_vuldet_reset_tables(db);

    sqlite3_close_v2(db);
    return 0;
}

int wm_vuldet_sql_error(sqlite3 *db, sqlite3_stmt *stmt) {
    mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
    wdb_finalize(stmt);
    sqlite3_close_v2(db);
    return OS_INVALID;
}

int wm_vuldet_remove_target_table(sqlite3 *db, char *TABLE, const char *target) {
    sqlite3_stmt *stmt = NULL;
    char sql[MAX_QUERY_SIZE];
    size_t size;

    if (size = snprintf(sql, MAX_QUERY_SIZE, vu_queries[VU_REMOVE_OS], TABLE), sql[size - 1] != ';') {
        sqlite3_close_v2(db);
        return OS_INVALID;
    }

    if (wm_vuldet_prepare(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, target, -1, NULL);

    if (wm_vuldet_step(stmt) != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    return 0;
}

int wm_vuldet_update_feed(update_node *upd, int8_t *updated) {
    int result;
    char feed_updated = 0;
    int pages_fail = 0;

    if (!upd->multi_path && (upd->dist_ref == FEED_NVD || upd->dist_ref == FEED_JREDHAT)) {
        int start;
        int end;

        if (!upd->multi_url) { // Online update for Redhat and NVD providers
            time_t n_date;
            struct tm *t_date;

            n_date = time(NULL);
            t_date = gmtime(&n_date);

            // Set the starting and ending point for each provider
            start = upd->dist_ref == FEED_NVD ? upd->update_from_year : 1;
            end = upd->dist_ref == FEED_NVD ? t_date->tm_year + 1900 : RED_HAT_REPO_MAX_REQ_ITS;
        } else { // Offline update from a custom repo for Redhat and NVD providers
            start = upd->multi_url_start;
            end = upd->multi_url_end;
        }

        // If it is a Red Hat update, we need to clean the database before we start
        if (upd->dist_ref == FEED_JREDHAT) {
            wm_vuldet_clean_rh(NULL);
        }

        for (upd->update_it = start; upd->update_it <= end; (upd->update_it)++) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, upd->dist_ref == FEED_NVD ? VU_UPDATING_NVD_YEAR : VU_UPDATING_RH_YEAR, upd->update_it);
            if (result = wm_vuldet_sync_feed(upd, updated), result) {
                // Feed synchronization failed
                if (upd->dist_ref == FEED_NVD) {
                    wm_vuldet_clean_nvd_year(NULL, upd->update_it);
                } else if (upd->dist_ref == FEED_JREDHAT) {
                    wm_vuldet_clean_rh(NULL);
                }
                return OS_INVALID;
            } else {
                if (upd->dist_ref == FEED_JREDHAT) {
                    switch (upd->update_state) {
                        case VU_FINISH_FETCH: // The last page has been reached
                            return 0;
                        case VU_TRY_NEXT_PAGE: // The maximum number of possible attempts for a page has been exhausted, the following page will be attempted
                            if (pages_fail == RED_HAT_REPO_MAX_FAIL_ITS) { // The allowed number of failed pages has been exhausted. The feed will not be updated.
                                mterror(WM_VULNDETECTOR_LOGTAG, VU_RH_REQ_FAIL_MAX, RED_HAT_REPO_MAX_FAIL_ITS);
                                wm_vuldet_clean_rh(NULL);
                                return OS_INVALID;
                            }
                            pages_fail++;
                        break;
                        case VU_INV_FEED:
                            wm_vuldet_clean_rh(NULL);
                            return OS_INVALID;
                        default:
                            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_DOWNLOAD_PAGE_SUC, upd->update_it);
                            feed_updated = 1;
                    }
                }
            }
        }

        if (upd->dist_ref == FEED_JREDHAT && !feed_updated) {
            return OS_INVALID;
        }
    } else {
        // single-providers -> offline update (multi_path only)
        // multi-providers -> online and offline update
        return wm_vuldet_sync_feed(upd, updated);
    }
    return 0;
}

int wm_vuldet_index_debian(sqlite3 *db, const char *target, update_node *update) {
    sqlite3_stmt *stmt = NULL;
    cJSON *deb_json = NULL;
    cJSON *package_json = NULL;
    cJSON *cve_json = NULL;
    cJSON *releases_json = NULL;
    cJSON *target_json = NULL;
    cJSON *status_json = NULL;
    cJSON *version_json = NULL;
    cJSON *urgency_json = NULL;
    cJSON *reason_json = NULL;
    char *target_lower = NULL;
    char *package = NULL;
    char *cve = NULL;
    char *version = NULL;
    int ignore = 0;

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DEB_STATUS_FEED);

    if (deb_json = wm_vuldet_get_debian_status_feed(update), !deb_json) {
        mtwarn(WM_VULNDETECTOR_LOGTAG, VU_DEB_STATUS_FEED_ERROR, DEBIAN_REPO_STATUS);
        return 0;
    }

    target_lower = w_tolower_str(target);

    for (package_json = deb_json->child; json_tagged_obj(package_json); package_json = package_json->next) {

        package = package_json->string;

        for (cve_json = package_json->child; json_tagged_obj(cve_json); cve_json = cve_json->next) {

            if (strncmp("CVE-", cve_json->string, 4)) {
                continue;
            }

            cve = cve_json->string;
            version = NULL;
            ignore = 0;

            if (releases_json = cJSON_GetObjectItem(cve_json, "releases"), !releases_json) {
                continue;
            }
            if (target_json = cJSON_GetObjectItem(releases_json, target_lower), !target_json) {
                continue;
            }
            status_json = cJSON_GetObjectItem(target_json, "status");
            version_json = cJSON_GetObjectItem(target_json, "fixed_version");
            urgency_json = cJSON_GetObjectItem(target_json, "urgency");
            reason_json = cJSON_GetObjectItem(target_json, "nodsa_reason");

            if (version_json && version_json->valuestring) {
                if (!strcmp(version_json->valuestring, "0")) {
                    ignore = 1; // Not affected
                } else {
                    version = version_json->valuestring; // Fixed
                }
            } else if (status_json && status_json->valuestring && !strcmp(status_json->valuestring, "open")) {
                if (urgency_json && urgency_json->valuestring && !strcmp(urgency_json->valuestring, "end-of-life")) {
                    ignore = 1; // Out of support
                } else if (urgency_json && urgency_json->valuestring && !strcmp(urgency_json->valuestring, "unimportant")) {
                    ignore = 1; // Not affected || Will not fix
                } else if (reason_json && reason_json->valuestring && !strcmp(reason_json->valuestring, "ignored")) {
                    ignore = 1; // Will not fix
                }
            }

            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_CVE], -1, &stmt, NULL) != SQLITE_OK) {
                os_free(target_lower);
                cJSON_Delete(deb_json);
                return wm_vuldet_sql_error(db, stmt);
            }

            sqlite3_bind_text(stmt, 1, cve, -1, NULL);
            sqlite3_bind_text(stmt, 2, target, -1, NULL);
            sqlite3_bind_text(stmt, 3, NULL, -1, NULL);
            sqlite3_bind_text(stmt, 4, package, -1, NULL);
            sqlite3_bind_text(stmt, 5, "less than", -1, NULL);
            sqlite3_bind_text(stmt, 6, version ? version : version_null, -1, NULL);
            sqlite3_bind_int(stmt, 7, 0);
            sqlite3_bind_int(stmt, 8, ignore);

            if (wm_vuldet_step(stmt) != SQLITE_DONE) {
                os_free(target_lower);
                cJSON_Delete(deb_json);
                return wm_vuldet_sql_error(db, stmt);
            }

            wdb_finalize(stmt);
        }
    }

    wdb_finalize(stmt);
    os_free(target_lower);
    cJSON_Delete(deb_json);

    return 0;
}

int wm_vuldet_insert_cve_info(wm_vuldet_db *parsed_oval, sqlite3 *db, sqlite3_stmt *stmt) {
    info_cve *info_it = parsed_oval->info_cves;
    int result;

    while (info_it) {
        if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_CVE_INFO], -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }

        sqlite3_bind_text(stmt, 1, info_it->cveid, -1, NULL);
        sqlite3_bind_text(stmt, 2, info_it->title, -1, NULL);
        sqlite3_bind_text(stmt, 3, (info_it->severity) ? info_it->severity : vu_severities[VU_UNKNOWN], -1, NULL);
        sqlite3_bind_text(stmt, 4, info_it->published, -1, NULL);
        sqlite3_bind_text(stmt, 5, info_it->updated, -1, NULL);
        sqlite3_bind_text(stmt, 6, parsed_oval->OS, -1, NULL);
        sqlite3_bind_text(stmt, 7, info_it->description, -1, NULL);
        sqlite3_bind_text(stmt, 8, info_it->cvss, -1, NULL);
        sqlite3_bind_text(stmt, 9, info_it->cvss_vector, -1, NULL);
        sqlite3_bind_text(stmt, 10, info_it->cvss3, -1, NULL);
        sqlite3_bind_text(stmt, 11, info_it->cvss3_vector, -1, NULL);
        sqlite3_bind_text(stmt, 12, info_it->cwe, -1, NULL);

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
            return wm_vuldet_sql_error(db, stmt);
        }
        wdb_finalize(stmt);

        // Saving the references corresponding to the current CVE
        int j;
        for (j = 0; info_it->refs && (j < info_it->refs->elements) && info_it->refs->values[j]; j++) {
            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_REF_INFO], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }
            sqlite3_bind_text(stmt, 1, info_it->cveid, -1, NULL);
            sqlite3_bind_text(stmt, 2, parsed_oval->OS, -1, NULL);
            sqlite3_bind_text(stmt, 3, info_it->refs->values[j], -1, NULL);
            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            wdb_finalize(stmt);
        }

        // Saving the bugzilla references corresponding to the current CVE
        for (j = 0; info_it->bugzilla_references && (j < info_it->bugzilla_references->elements) && info_it->bugzilla_references->values[j]; j++) {
            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_BUG_REF_INFO], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }
            sqlite3_bind_text(stmt, 1, info_it->cveid, -1, NULL);
            sqlite3_bind_text(stmt, 2, parsed_oval->OS, -1, NULL);
            sqlite3_bind_text(stmt, 3, info_it->bugzilla_references->values[j], -1, NULL);
            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            wdb_finalize(stmt);
        }


        // Saving the advisories corresponding to the current CVE
        for (j = 0; info_it->advisories && (j < info_it->advisories->elements) && info_it->advisories->values[j]; j++) {
            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_ADVISORY_INFO], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }
            sqlite3_bind_text(stmt, 1, info_it->cveid, -1, NULL);
            sqlite3_bind_text(stmt, 2, parsed_oval->OS, -1, NULL);
            sqlite3_bind_text(stmt, 3, info_it->advisories->values[j], -1, NULL);
            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            wdb_finalize(stmt);
        }

        info_it = info_it->prev;
    }

    return 0;
}

int wm_vuldet_insert(wm_vuldet_db *parsed_oval, update_node *update) {
    sqlite3 *db;
    sqlite3_stmt *stmt = NULL;
    int result;
    const char *query;
    oval_metadata *met_it = &parsed_oval->metadata;
    vulnerability *vul_it = parsed_oval->vulnerabilities;
    info_state *state_it = parsed_oval->info_states;
    info_obj *obj_it = parsed_oval->info_objs;
    info_test *test_it = parsed_oval->info_tests;
    cpe_list *cpes_it = parsed_oval->nvd_cpes;
    nvd_vulnerability *nvd_it = parsed_oval->nvd_vulnerabilities;
    vu_cpe_dic *w_cpes_it = parsed_oval->w_cpes;
    vu_msu_entries *msu_it = &parsed_oval->msu;
    variables *vars_it = parsed_oval->vars;

    if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_exec(db, vu_queries[BEGIN_T], NULL, NULL, NULL);

    switch (update->dist_ref) {
        case FEED_REDHAT:
            if (wm_vuldet_remove_target_table(db, ARCHITECTURES_TABLE, parsed_oval->OS) ||
                wm_vuldet_remove_target_table(db, CVE_TABLE, parsed_oval->OS)) {
                return OS_INVALID;
            }
            break;
        case FEED_UBUNTU:
        case FEED_DEBIAN:
            if (wm_vuldet_remove_target_table(db, CVE_TABLE, parsed_oval->OS)      ||
                wm_vuldet_remove_target_table(db, METADATA_TABLE, parsed_oval->OS) ||
                wm_vuldet_remove_target_table(db, CVE_INFO_TABLE, parsed_oval->OS) ||
                wm_vuldet_remove_target_table(db, VARIABLES_TABLE, parsed_oval->OS)) {
                return OS_INVALID;
            }
        break;
        case FEED_CPEW:
            if (wm_vuldet_clean_wcpe(db)) {
                return OS_INVALID;
            }
        break;
        case FEED_ARCH:
            if (wm_vuldet_remove_target_table(db, CVE_TABLE, parsed_oval->OS) ||
                wm_vuldet_remove_target_table(db, CVE_INFO_TABLE, parsed_oval->OS)) {
                return OS_INVALID;
            }
            break;
        case FEED_NVD: case FEED_MSU: case FEED_JREDHAT:
        break;
        default:
            sqlite3_close_v2(db);
            return OS_INVALID;
    }

    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_VU);

    if (cpes_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_CPES_SEC);

        if (wm_vuldet_insert_cpe_db(db, cpes_it, 1)){
            mterror(WM_VULNDETECTOR_LOGTAG, VU_CPES_INSERT_ERROR);
            wm_vuldet_free_cpe_list(cpes_it);
            free(cpes_it);
            sqlite3_close_v2(db);
            return OS_INVALID;
        }
        wm_vuldet_free_cpe_list(cpes_it);
        free(cpes_it);
    }

    if (w_cpes_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_CPES_DIC);

        if (wm_vuldet_insert_cpe_dic(db, w_cpes_it)){
            mterror(WM_VULNDETECTOR_LOGTAG, VU_CPES_INSERT_ERROR);
            return OS_INVALID;
        }
        free(w_cpes_it);
    }

    if (msu_it->vul) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_MSU);

        if (wm_vuldet_insert_MSU(db, msu_it)){
            return OS_INVALID;
        }
    }

    if (nvd_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_VUL_SEC, "NVD");
        if (wm_vuldet_index_nvd(db, update, nvd_it)) {
            return OS_INVALID;
        }
        parsed_oval->nvd_vulnerabilities = NULL;
    }

    // Adds the vulnerabilities
    if (vul_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_VUL_SEC, update->dist_ext);
    }

    while (vul_it) {
        // If you do not have this field, it has been discarded by the preparser and the OS is not affected
        if (vul_it->state_id) {
            if (vul_it->rhsa_list) {
                wm_vuldet_oval_traverse_rhsa(vul_it);
            }

            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_CVE], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }

            sqlite3_bind_text(stmt, 1, vul_it->cve_id, -1, NULL);
            sqlite3_bind_text(stmt, 2, parsed_oval->OS, -1, NULL);
            sqlite3_bind_text(stmt, 3, NULL, -1, NULL);
            sqlite3_bind_text(stmt, 5, vul_it->state_id, -1, NULL);
            if (vul_it->package_name && vul_it->package_version) {
                    sqlite3_bind_text(stmt, 4, vul_it->package_name, -1, NULL);
                    sqlite3_bind_text(stmt, 6, vul_it->package_version, -1, NULL);
            } else {
                sqlite3_bind_text(stmt, 4, vul_it->package_name ? vul_it->package_name : vul_it->state_id, -1, NULL);
                sqlite3_bind_text(stmt, 6, NULL, -1, NULL);
            }
            sqlite3_bind_int(stmt, 7, 0);
            sqlite3_bind_int(stmt, 8, vul_it->ignore);
            sqlite3_bind_int(stmt, 9, 0);

            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }

            wdb_finalize(stmt);
        }

        vulnerability *vul_aux = vul_it;
        vul_it = vul_it->prev;
        free(vul_aux->cve_id);
        free(vul_aux->state_id);
        free(vul_aux->package_name);
        os_free(vul_aux->package_version);
        free(vul_aux);
    }

    // Adds Debian vulnerabilities
    if (update->dist_ref == FEED_DEBIAN) {
        if (wm_vuldet_index_debian(db, vu_feed_tag[update->dist_tag_ref], update) == OS_INVALID) {
            return wm_vuldet_sql_error(db, stmt);
        }
    }

    if (test_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_TEST_SEC, update->dist_ext);
    }

    // Links vulnerabilities to their conditions
    while (test_it) {
        if (wm_vuldet_prepare(db, vu_queries[VU_UPDATE_CVE], -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }

        sqlite3_bind_text(stmt, 1, test_it->obj, -1, NULL);
        sqlite3_bind_text(stmt, 2, test_it->state, -1, NULL);
        sqlite3_bind_text(stmt, 3, test_it->id, -1, NULL);
        result = wm_vuldet_step(stmt);

        switch (result) {
        case SQLITE_DONE:
            break;
        case SQLITE_CONSTRAINT:
            wdb_finalize(stmt);

            if (wm_vuldet_prepare(db, vu_queries[VU_UPDATE_CVE_NOT_FIXED], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }

            sqlite3_bind_text(stmt, 1, test_it->obj, -1, NULL);
            sqlite3_bind_text(stmt, 2, vu_package_comp[VU_COMP_L], -1, NULL);
            sqlite3_bind_text(stmt, 3, version_null, -1, NULL);
            sqlite3_bind_text(stmt, 4, test_it->id, -1, NULL);

            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            break;
        default:
            return wm_vuldet_sql_error(db, stmt);
        }

        wdb_finalize(stmt);

        info_test *test_aux = test_it;
        test_it = test_it->prev;
        free(test_aux->id);
        free(test_aux->state);
        free(test_aux->obj);
        free(test_aux);
    }

    if (state_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_VU_CO, update->dist_ext);

        sqlite3_exec(db, vu_queries[VU_REMOVE_UNUSED_VULS], NULL, NULL, NULL);
    }

    // Sets the OVAL operators and values
    int arch_id = 0;

    while (state_it) {
        if (state_it->operation_value != NULL) {

            // Get ID to insert architectures
            if (update->dist_ref == FEED_REDHAT) {
                arch_id++;
            }

            // Replace the state ID by the real values
            query = vu_queries[VU_UPDATE_CVE_VAL];
            if (wm_vuldet_prepare(db, query, -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }

            sqlite3_bind_text(stmt, 1, state_it->operation, -1, NULL);

            // If the epoch is 0, we don't save it because it is not necessary
            if (!strcmp(state_it->operation_value, version_null) || strncmp("0:", state_it->operation_value, 2)) {
                sqlite3_bind_text(stmt, 2, state_it->operation_value, -1, NULL);
            } else {
                sqlite3_bind_text(stmt, 2, (state_it->operation_value + 2), -1, NULL);
            }

            sqlite3_bind_int(stmt, 3, arch_id);
            sqlite3_bind_text(stmt, 4, state_it->id, -1, NULL);

            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            wdb_finalize(stmt);

            // Insert architecture values
            if (arch_id && state_it->arch) {
                for (int i = 0; state_it->arch[i]; i++) {
                    if (wm_vuldet_prepare(db, vu_queries[VU_UPDATE_ARCH], -1, &stmt, NULL) != SQLITE_OK) {
                        return wm_vuldet_sql_error(db, stmt);
                    }

                    sqlite3_bind_int(stmt, 1, arch_id);
                    sqlite3_bind_text(stmt, 2, parsed_oval->OS, -1, NULL);
                    sqlite3_bind_text(stmt, 3, state_it->arch[i], -1, NULL);

                    if (result = wm_vuldet_step(stmt), result != SQLITE_DONE) {
                        return wm_vuldet_sql_error(db, stmt);
                    }
                    wdb_finalize(stmt);
                }
            }
        }

        info_state *state_aux = state_it;
        state_it = state_it->prev;
        os_free(state_aux->id);
        os_free(state_aux->operation);
        os_free(state_aux->operation_value);
        w_FreeArray(state_aux->arch);
        os_free(state_aux->arch);
        os_free(state_aux);
    }

    if (obj_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_PACK_NAME, update->dist_ext);
    }

    // Sets the OVAL package name
    while (obj_it) {
        char *normalized_name = NULL;

        if (obj_it->obj) {
            if (result = wm_vuldet_prepare(db, vu_queries[VU_UPDATE_CVE_PACK], -1, &stmt, NULL), result != SQLITE_OK && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            normalized_name = w_tolower_str(obj_it->obj);
            sqlite3_bind_text(stmt, 1, normalized_name, -1, NULL);
            sqlite3_bind_int(stmt, 2, obj_it->need_vars);
            sqlite3_bind_text(stmt, 3, obj_it->id, -1, NULL);
        } else {
            if (result = wm_vuldet_prepare(db, vu_queries[VU_REMOVE_UNUSED_ID], -1, &stmt, NULL), result != SQLITE_OK && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            sqlite3_bind_text(stmt, 1, obj_it->id, -1, NULL);
        }

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE) {
            os_free(normalized_name);
            return wm_vuldet_sql_error(db, stmt);
        }

        wdb_finalize(stmt);
        os_free(normalized_name);

        info_obj *obj_aux = obj_it;
        obj_it = obj_it->prev;
        free(obj_aux->id);
        free(obj_aux->obj);
        free(obj_aux);
    }

    if (vars_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_VARIABLES, update->dist_ext);
    }

    // Sets the OVAL variables
    while (vars_it) {
        char *normalized_id = NULL;
        query = vu_queries[VU_INSERT_VARIABLES];

        if (vars_it->id) {
            int j;
            normalized_id = w_tolower_str(vars_it->id);

            for (j = 0; vars_it->values[j]; j++) {
                char *normalized_name = NULL;
                if (wm_vuldet_prepare(db, query, -1, &stmt, NULL) != SQLITE_OK) {
                    os_free(normalized_id);
                    return wm_vuldet_sql_error(db, stmt);
                }
                sqlite3_bind_text(stmt, 1, normalized_id, -1, NULL);
                normalized_name = w_tolower_str(vars_it->values[j]);
                sqlite3_bind_text(stmt, 2, normalized_name, -1, NULL);
                sqlite3_bind_text(stmt, 3, parsed_oval->OS, -1, NULL);
                if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                    os_free(normalized_id);
                    os_free(normalized_name);
                    return wm_vuldet_sql_error(db, stmt);
                }
                wdb_finalize(stmt);
                os_free(normalized_name);
            }
        }
        os_free(normalized_id);

        variables *var_aux = vars_it;
        vars_it = vars_it->prev;
        free(var_aux->id);
        w_FreeArray(var_aux->values);
        free(var_aux->values);
        free(var_aux);
    }

    // Discard the CVE info from the RHEL OVALs
    if (parsed_oval->info_cves && (update->dist_ref != FEED_REDHAT)) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_VU_INFO, update->dist_ext);
        if (result = wm_vuldet_insert_cve_info(parsed_oval, db, stmt), result) {
            return result;
        }
    }

    // Clean the unresolved states for RHEL5 entries
    if (update->dist_tag_ref == FEED_RHEL5) {
        sqlite3_exec(db, vu_queries[VU_REMOVE_UNUSED_STATES], NULL, NULL, NULL);
    }

    wm_vuldet_clean_vulnerability_info(parsed_oval);

    if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_METADATA], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, parsed_oval->OS, -1, NULL);
    sqlite3_bind_text(stmt, 2, met_it->product_name, -1, NULL);
    sqlite3_bind_text(stmt, 3, met_it->product_version, -1, NULL);
    sqlite3_bind_text(stmt, 4, met_it->schema_version, -1, NULL);
    sqlite3_bind_text(stmt, 5, met_it->timestamp, -1, NULL);
    sqlite3_bind_text(stmt, 6, NULL, -1, NULL);
    sqlite3_bind_int(stmt, 7, 0);
    sqlite3_bind_int(stmt, 8, 0);

    if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    free(met_it->product_name);
    free(met_it->product_version);
    free(met_it->schema_version);
    free(met_it->timestamp);

    sqlite3_exec(db, vu_queries[END_T], NULL, NULL, NULL);
    sqlite3_close_v2(db);
    return 0;
}

int wm_vuldet_check_db() {
    if (wm_vuldet_create_file(CVE_DB, schema_vuln_detector_sql)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INVALID_DB_INT);
        pthread_exit(NULL);
    }
    return 0;
}

void wm_vuldet_add_vulnerability_info(wm_vuldet_db *ctrl_block) {
    info_cve *new;
    os_calloc(1, sizeof(info_cve), new);

    if (ctrl_block->info_cves) {
        new->prev = ctrl_block->info_cves;
    }
    ctrl_block->info_cves = new;
}

void wm_vuldet_clean_vulnerability_info(wm_vuldet_db *ctrl_block) {
    info_cve *info_aux = NULL;
    info_cve *info_it = ctrl_block->info_cves;

    while (info_it) {
        if (info_it->refs)
            free_strarray(info_it->refs->values);

        if (info_it->bugzilla_references)
            free_strarray(info_it->bugzilla_references->values);

        if (info_it->advisories)
            free_strarray(info_it->advisories->values);

        info_aux = info_it->prev;
        free(info_it->cveid);
        free(info_it->title);
        free(info_it->severity);
        free(info_it->published);
        free(info_it->updated);
        free(info_it->refs);
        free(info_it->description);
        free(info_it->cvss);
        free(info_it->cvss_vector);
        free(info_it->cvss3);
        free(info_it->cvss3_vector);
        free(info_it->bugzilla_references);
        free(info_it->advisories);
        free(info_it->cwe);
        free(info_it);
        info_it = info_aux;
    }
}

void wm_vuldet_add_oval_vulnerability(wm_vuldet_db *ctrl_block) {
    vulnerability *new;
    os_calloc(1, sizeof(vulnerability), new);

    if (ctrl_block->vulnerabilities) {
        new->prev = ctrl_block->vulnerabilities;
    }
    ctrl_block->vulnerabilities = new;
}

char *wm_vuldet_oval_xml_preparser(char *path, vu_feed dist) {
    FILE *input, *output = NULL;
    char buffer[OS_MAXSTR + 1];
    parser_state state = V_START;
    char *found;
    char *tmp_file;

    os_strdup(VU_FIT_TEMP_FILE, tmp_file);

    if (input = fopen(path, "r" ), !input) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_OPEN_FILE_ERROR, path);
        os_free(tmp_file);
        goto free_mem;
    } else if (output = fopen(tmp_file, "w" ), !output) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_OPEN_FILE_ERROR, tmp_file);
        os_free(tmp_file);
        goto free_mem;
    }

    while (fgets(buffer, OS_MAXSTR, input)) {
        if (dist == FEED_DEBIAN) {
            // For Debian, remove packages information since the JSON feed will be used to get that information.
            switch (state) {
                case V_OVALDEFINITIONS:
                    if (found = strstr(buffer, "<definitions"), found) {
                        state = V_DEFINITIONS;
                    } else if (found = strstr(buffer, "<generator"), found) {
                        state = V_GENERATOR;
                    } else if (found = strstr(buffer, "</oval_definitions>"), found) {
                        state = V_END;
                    } else {
                        continue;
                    }
                break;
                case V_DEFINITIONS:
                    if (found = strstr(buffer, "<definition"), found) {
                        state = V_DEFINITION;
                    } else if (found = strstr(buffer, "</definitions>"), found) {
                        state = V_OVALDEFINITIONS;
                    }
                break;
                case V_DEFINITION:
                    if (found = strstr(buffer, "<metadata"), found) {
                        state = V_METADATA;
                    } else if (found = strstr(buffer, "</definition>"), found) {
                        state = V_DEFINITIONS;
                    } else {
                        continue;
                    }
                break;
                case V_METADATA:
                    if (found = strstr(buffer, "</metadata>"), found) {
                        state = V_DEFINITION;
                    }
                break;
                case V_GENERATOR:
                    if (found = strstr(buffer, "</generator>"), found) {
                        state = V_OVALDEFINITIONS;
                    }
                break;
                default:
                    if (found = strstr(buffer, "<oval_definitions"), found) {
                        state = V_OVALDEFINITIONS;
                    } else {
                        continue;
                    }
            }

        } else if (dist == FEED_REDHAT) { // 5.10
            switch (state) {
            case V_START:
                // Discard the first line of the XML
                if (strstr(buffer, "?>")) {
                    state = V_STATES;
                }
                continue;
            case V_CRITERIA:
                /* Every package affected by a CVE has a duplicated entry
                * specifying it was signed with redhatrelease key. Since it's
                * not useful, we can remove it. */
                if (strstr(buffer, "is signed with Red Hat")) {
                    state = V_STATES;
                    continue;
                }
                if (strstr(buffer, "</criteria>")) {
                    state = V_STATES;
                }
            break;
            case V_DEFINITIONS:
                if (found = strstr(buffer, "</definition"), found) {
                    state = V_END;
                }
                continue;
            default:
                /* The XML parser fails to analyse correctly this section.
                * This is just a workaround, for more information -> Issues #5300 and #5299. */
                if (strstr(buffer, "<![CDATA") ||
                    strstr(buffer, "<ind-def:pattern operation=\"pattern match\">")) {
                    continue;

                } else if (strstr(buffer, "<criteria ")) {
                    state = V_CRITERIA;

                // Discard not-affected packages
                } else if (strstr(buffer, "<definition") &&
                    strstr(buffer, "unaffected")) {
                    state = V_DEFINITIONS;
                    continue;
                }
            }

        } else if (dist != FEED_UBUNTU) {
            os_free(tmp_file);
            goto free_mem;
        }

        fwrite(buffer, 1, strlen(buffer), output);
    }

free_mem:
    if (input) {
        fclose(input);
    }
    if (output) {
        fclose(output);
    }

    return tmp_file;
}

references *wm_vuldet_extract_advisories(cJSON *advisories) {
    references *advisories_data = NULL;

    if (advisories) {
        os_calloc(1, sizeof(references), advisories_data);

        for (; advisories && advisories->valuestring; advisories = advisories->next) {
            os_realloc(advisories_data->values, (advisories_data->elements + 2) * sizeof(char *), advisories_data->values);
            w_strdup(advisories->valuestring, advisories_data->values[advisories_data->elements]);
            advisories_data->values[++advisories_data->elements] = NULL;
        }
    }

    return advisories_data;
}

void wm_vuldet_adapt_title(char *title, char *cve) {
    // Remove unnecessary line jumps and spaces
    size_t size;
    int offset;
    char *title_ofs;

    for (size = strlen(title) - 1; size > 0 && title[size] == ' '; size -= 1) {
        title[size] = '\0';
    }

    if (title[size] == '\n') {
        title[size--] = '\0';
    }

    offset = title[0] == '\n' ? 1 : 0;
    if(size > 1 && !strncmp(title + offset, cve, strlen(cve))) {
        offset += strlen(cve) + 1;
    }
    os_strdup(title + offset, title_ofs);
    strncpy(title, title_ofs, strlen(title_ofs) + 1);
    free(title_ofs);
}

void wm_vuldet_oval_copy_rhsa(vulnerability *old, vulnerability *new) {
    if (!old->rhsa_list) {
        return;
    }

    for (int i = 0; old->rhsa_list[i]; i++) {
        wm_vuldet_oval_append_rhsa(new, old->rhsa_list[i]);
    }
}

void wm_vuldet_oval_traverse_rhsa(vulnerability *vul_it) {
   vulnerability *new = NULL;

    // They new vulnerabilities will hold the same information as the original one,
    // but pointing to a different CVE.
    for (int i = 0; vul_it->rhsa_list[i]; i++) {
        os_calloc(1, sizeof(vulnerability), new);
        os_strdup(vul_it->rhsa_list[i], new->cve_id);
        os_strdup(vul_it->state_id, new->state_id);
        new->prev = vul_it->prev;
        vul_it->prev = new;
        os_free(vul_it->rhsa_list[i]);
    }

    os_free(vul_it->rhsa_list);
}

void wm_vuldet_oval_append_rhsa(vulnerability *vul_it, char *cve) {
    static int index = 0;

    // Reset the index at the arrival of a new vulnerability.
    index = (!vul_it->rhsa_list)? 0 : index;

    // The list will increase dynamically by 5.
    if ((index % RHSA_DEFAULT_NUM) == 0) {
        os_realloc(vul_it->rhsa_list, (RHSA_DEFAULT_NUM + index + 1) * sizeof(char *), vul_it->rhsa_list);
        memset(&vul_it->rhsa_list[index + 1], 0, RHSA_DEFAULT_NUM * sizeof(char *));
    }

    os_strdup(cve, vul_it->rhsa_list[index]);
    index++;
}

int wm_vuldet_oval_xml_parser(OS_XML *xml, XML_NODE node, wm_vuldet_db *parsed_oval, update_node *update, vu_logic condition) {
    int i, j;
    int retval = 0;
    XML_NODE chld_node = NULL;
    vu_feed dist = update->dist_ref;
    vu_feed dist_ref = update->dist_tag_ref;

    static const char *XML_OVAL_DEFINITIONS = "oval_definitions";
    // Generator
    static const char *XML_GENERATOR = "generator";
    static const char *XML_OVAL_PRODUCT_NAME = "oval:product_name";
    static const char *XML_OVAL_PRODUCT_VERSION = "oval:product_version";
    static const char *XML_OVAL_SCHEMA_VERSION = "oval:schema_version";
    static const char *XML_OVAL_TIMESTAMP = "oval:timestamp";
    // Definitions
    static const char *XML_DEFINITIONS = "definitions";
    static const char *XML_DEFINITION = "definition";
    static const char *XML_CLASS = "class";
    static const char *XML_VULNERABILITY = "vulnerability";
    static const char *XML_PATCH = "patch"; // rhel
    static const char *XML_ID = "id";
    static const char *XML_METADATA = "metadata";
    static const char *XML_TITLE = "title";
    static const char *XML_DESCRIPTION = "description";
    static const char *XML_REFERENCE = "reference";
    static const char *XML_REF_ID = "ref_id";
    static const char *XML_REF_URL = "ref_url";
    static const char *XML_ADVISORY = "advisory";
    static const char *XML_SEVERITY = "severity";
    static const char *XML_PUBLIC_DATE = "public_date";
    static const char *XML_UPDATED = "updated";
    static const char *XML_BUG = "bug";
    static const char *XML_REF = "ref";
    static const char *XML_DEBIAN = "debian";
    static const char *XML_DATE = "date";
    static const char *XML_CRITERIA = "criteria";
    static const char *XML_OPERATOR = "operator";
    static const char *XML_OR = "OR";
    static const char *XML_AND = "AND";
    static const char *XML_CRITERION = "criterion";
    static const char *XML_TEST_REF = "test_ref";
    static const char *XML_COMMENT = "comment";
    // Tests
    static const char *XML_TESTS = "tests";
    static const char *XML_RPM_LINUX_INFO_TEST = "red-def:rpminfo_test";
    static const char *XML_RPM_LINUX_INFO_OBJ = "red-def:rpminfo_object";
    static const char *XML_DPKG_LINUX_INFO_TEST = "linux-def:dpkginfo_test";
    static const char *XML_LINUX_OBJ = "linux-def:object";
    static const char *XML_LINUX_RPM_OBJ = "red-def:object";
    static const char *XML_OBJECT_REF = "object_ref";
    static const char *XML_LINUX_STATE = "linux-def:state";
    static const char *XML_STATE_REF = "state_ref";
    // Objects
    static const char *XML_OBJECTS = "objects";
    static const char *XML_DPKG_LINUX_INFO_OBJ = "linux-def:dpkginfo_object";
    static const char *XML_LINUX_NAME = "linux-def:name";
    static const char *XML_LINUX_RPM_STATE = "red-def:state";
    static const char *XML_LINUX_RPM_NAME = "red-def:name";
    static const char *XML_VAR_REF = "var_ref";
    static const char *XML_VAR_CHECK = "var_check";
    static const char *XML_STATES = "states";
    static const char *XML_RPM_LINUX_INFO_STATE = "red-def:rpminfo_state";
    static const char *XML_DPKG_LINUX_INFO_STATE = "linux-def:dpkginfo_state";
    static const char *XML_LINUX_DEF_EVR = "linux-def:evr";
    static const char *XML_LINUX_RPM_DEF_EVR = "red-def:evr";
    static const char *XML_OPERATION = "operation";
    static const char *XML_DATATYPE = "datatype";
    static const char *XML_RPM_ARCHITECTURE = "red-def:arch";
    // Variables
    static const char *XML_VARIABLES = "variables";
    static const char *XML_CONST_VAR = "constant_variable";
    static const char *XML_VALUE = "value";

    for (i = 0; node[i]; i++) {
        chld_node = NULL;
        if (!node[i]->element) {
            mterror(WM_VULNDETECTOR_LOGTAG, XML_ELEMNULL);
            return OS_INVALID;
        }

        if ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_DPKG_LINUX_INFO_STATE)) ||
            (dist == FEED_REDHAT && !strcmp(node[i]->element, XML_RPM_LINUX_INFO_STATE))) {
            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                goto invalid_elem;
            }
            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_ID)) {
                    info_state *infos;
                    os_calloc(1, sizeof(info_state), infos);
                    os_strdup(node[i]->values[j], infos->id);
                    infos->operation = infos->operation_value = NULL;
                    infos->prev = parsed_oval->info_states;
                    parsed_oval->info_states = infos;
                    if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, condition) == OS_INVALID) {
                        goto end;
                    }
                }
            }
        } else if ((dist == FEED_UBUNTU || dist == FEED_REDHAT) && !strcmp(node[i]->element, XML_CONST_VAR)) {
            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                goto invalid_elem;
            }
            if (node[i]->attributes && node[i]->values) {
                variables *vars = NULL;

                for (j = 0; node[i]->attributes[j] && node[i]->values[j]; j++) {
                    if (!strcmp(node[i]->attributes[j], XML_ID)) {
                        os_calloc(1, sizeof(variables), vars);
                        os_strdup(node[i]->values[j], vars->id);
                        vars->prev = parsed_oval->vars;
                        parsed_oval->vars =vars;

                        for (j = 0; chld_node[j] && chld_node[j]->element &&
                                    !strcmp(chld_node[j]->element, XML_VALUE) &&
                                    chld_node[j]->content; j++) {
                            os_realloc(vars->values, (j + 2) * sizeof(char *), vars->values);
                            os_strdup(chld_node[j]->content, vars->values[j]);
                            vars->values[j + 1] = NULL;
                        }

                        vars->elements = j;
                        break;
                    }
                }
            }
        }
        else if ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_DPKG_LINUX_INFO_TEST)) ||
                (dist == FEED_REDHAT && !strcmp(node[i]->element, XML_RPM_LINUX_INFO_TEST))) {
            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                goto invalid_elem;
            }
            info_test *infot;
            os_calloc(1, sizeof(info_test), infot);
            infot->state = NULL;
            infot->prev = parsed_oval->info_tests;
            parsed_oval->info_tests = infot;

            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_ID)) {
                    os_strdup(node[i]->values[j], parsed_oval->info_tests->id);
                }
            }
            if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, VU_PACKG) == OS_INVALID) {
                goto end;
            }
        }

        else if ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_DPKG_LINUX_INFO_OBJ)) ||
                 (dist == FEED_REDHAT && !strcmp(node[i]->element, XML_RPM_LINUX_INFO_OBJ))) {
            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                goto invalid_elem;
            }

            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_ID)) {
                    info_obj *info_o;
                    os_calloc(1, sizeof(info_obj), info_o);
                    os_strdup(node[i]->values[j], info_o->id);
                    info_o->prev = parsed_oval->info_objs;
                    parsed_oval->info_objs = info_o;
                    if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, VU_OBJ) == OS_INVALID) {
                        goto end;
                    }
                }
            }
            if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, VU_PACKG) == OS_INVALID) {
                goto end;
            }
        } else if (condition == VU_OBJ && ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_LINUX_NAME)) ||
                    (dist == FEED_REDHAT && !strcmp(node[i]->element, XML_LINUX_RPM_NAME)))) {
            if (node[i]->content && *node[i]->content) {
                w_strdup(node[i]->content, parsed_oval->info_objs->obj);
            } else {
                if (node[i]->attributes && node[i]->values) {
                    int j;
                    char *var_check = NULL;
                    char *var_ref = NULL;

                    for (j = 0; node[i]->attributes[j] && node[i]->values[j]; j++) {
                        if (!strcmp(node[i]->attributes[j], XML_VAR_REF)) {
                            if (!var_ref) {
                                os_strdup(node[i]->values[j], var_ref);
                            }
                        } else if (!strcmp(node[i]->attributes[j], XML_VAR_CHECK)) {
                            if (!var_check) {
                                os_strdup(node[i]->values[j], var_check);
                            }
                        }
                    }

                    if (!var_check || !var_ref) {
                        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_OVAL_OBJ_INV, "Parameters 'var_check' and 'var_ref' were expected");
                    } else {
                        if (!strcmp(var_check, "at least one")) {
                            parsed_oval->info_objs->need_vars = 1;
                            parsed_oval->info_objs->obj = var_ref;
                            var_ref = NULL;
                        } else {
                            char error_msg[OS_SIZE_128];
                            snprintf(error_msg, OS_SIZE_128, "Unexpected var_check: '%s'", var_check);
                            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_OVAL_OBJ_INV, error_msg);
                        }
                    }
                    free(var_ref);
                    free(var_check);
                } else {
                    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_OVAL_OBJ_INV, "Empty object");
                }
            }
        } else if ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_LINUX_DEF_EVR)) ||
                    (dist == FEED_REDHAT && !strcmp(node[i]->element, XML_LINUX_RPM_DEF_EVR))) {
            if (node[i]->attributes && *node[i]->attributes && node[i]->content && *node[i]->content) {
                if (dist_ref == FEED_RHEL5) {
                    // The RHEL5 OVAL contains vulnerable packages of others platforms
                    // We are only interested on RHEL5 packages from this feed
                    if (!strstr(node[i]->content, vu_package_dist[VU_RH_EXT_5])) {
                        // A step backward in order to continue the parse process
                        info_state *state_prev_ptr;
                        state_prev_ptr = parsed_oval->info_states->prev;
                        os_free(parsed_oval->info_states->id);
                        os_free(parsed_oval->info_states);
                        parsed_oval->info_states= state_prev_ptr;

                        goto end;
                    }
                }
                for (j = 0; node[i]->attributes[j]; j++) {
                    if (!strcmp(node[i]->attributes[j], XML_OPERATION)) {
                        os_strdup(node[i]->values[j], parsed_oval->info_states->operation);
                        os_strdup(node[i]->content, parsed_oval->info_states->operation_value);
                    }
                }
                if (!parsed_oval->info_states->operation && !strcmp(*node[i]->attributes, XML_DATATYPE) && !strcmp(*node[i]->values, "version")) {
                    os_strdup(vu_package_comp[VU_COMP_EQ], parsed_oval->info_states->operation);
                    os_strdup(node[i]->content, parsed_oval->info_states->operation_value);
                }

            }
        } else if (dist == FEED_REDHAT && !strcmp(node[i]->element, XML_RPM_ARCHITECTURE)) {
            if (node[i]->attributes && *node[i]->attributes && node[i]->content && *node[i]->content) {
                os_calloc(1, sizeof(char *), parsed_oval->info_states->arch);
                for (j = 0; node[i]->attributes[j]; j++) {
                    if (!strcmp(node[i]->attributes[j], XML_OPERATION)) {
                        if (node[i]->values[j] && !strcmp(node[i]->values[j], "equals")) {
                            os_strdup(node[i]->content, parsed_oval->info_states->arch[0]);
                        } else if (node[i]->values[j] && !strcmp(node[i]->values[j], "pattern match")) {
                            char * archs = NULL;
                            char * arch_item = NULL;
                            char * saveptr = NULL;
                            int k = 0;
                            os_strdup(node[i]->content, archs);
                            arch_item = strtok_r(archs, "|", &saveptr);
                            while (arch_item) {
                                k++;
                                os_realloc(parsed_oval->info_states->arch, (k + 1) * sizeof(char *), parsed_oval->info_states->arch);
                                os_strdup(arch_item, parsed_oval->info_states->arch[k - 1]);
                                parsed_oval->info_states->arch[k] = NULL;
                                arch_item = strtok_r(NULL, "|", &saveptr);
                            }
                            os_free(archs);
                        }
                    }
                }
                if (!parsed_oval->info_states->arch[0]) {
                    os_strdup("noarch", parsed_oval->info_states->arch[0]);
                }
            }
        } else if ((condition == VU_PACKG) &&
                   ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_LINUX_STATE)) ||
                   (dist == FEED_REDHAT && !strcmp(node[i]->element, XML_LINUX_RPM_STATE)))) {
            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_STATE_REF)) {
                    if (!parsed_oval->info_tests->state) {
                        os_strdup(node[i]->values[j], parsed_oval->info_tests->state);
                    }
                }
            }
        } else if ((condition == VU_PACKG) &&
                   ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_LINUX_OBJ)) ||
                   (dist == FEED_REDHAT && !strcmp(node[i]->element, XML_LINUX_RPM_OBJ)))) {
            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_OBJECT_REF)) {
                    if (!parsed_oval->info_tests->obj) {
                        os_strdup(node[i]->values[j], parsed_oval->info_tests->obj);
                    }
                }
            }
        }
        else if (!strcmp(node[i]->element, XML_DEFINITION)) {
            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                goto invalid_elem;
            }
            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_CLASS) &&
                    (!strcmp(node[i]->values[j], XML_VULNERABILITY) ||
                    !strcmp(node[i]->values[j], XML_PATCH))) {
                    wm_vuldet_add_oval_vulnerability(parsed_oval);
                    wm_vuldet_add_vulnerability_info(parsed_oval);
                    if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, condition) == OS_INVALID) {
                        retval = OS_INVALID;
                        goto end;
                    }
                }
            }
        }
        else if (!strcmp(node[i]->element, XML_REFERENCE)) {
            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_REF_ID) &&
                    strstr(node[i]->values[j], "CVE")) {
                    // Create new CVE structure when a new reference appears.
                    // Append the new CVE name to the already existing vulnerability.
                    if (parsed_oval->vulnerabilities->cve_id) {
                        info_cve *new;
                        os_calloc(1, sizeof(info_cve), new);
                        new->prev = parsed_oval->info_cves->prev;
                        parsed_oval->info_cves->prev = new;
                        os_strdup(node[i]->values[j], new->cveid);
                        wm_vuldet_oval_append_rhsa(parsed_oval->vulnerabilities, node[i]->values[j]);
                    } else {
                        os_strdup(node[i]->values[j], parsed_oval->vulnerabilities->cve_id);
                        os_strdup(node[i]->values[j], parsed_oval->info_cves->cveid);
                    }

                } else if (!strcmp(node[i]->attributes[j], XML_REF_URL)) {
                    if (!parsed_oval->info_cves->refs)
                        os_calloc(1, sizeof(references), parsed_oval->info_cves->refs);

                    references *refs = parsed_oval->info_cves->refs;
                    os_realloc(refs->values, (refs->elements + 2) * sizeof(char *), refs->values);
                    os_strdup(node[i]->values[j], refs->values[refs->elements]);
                    refs->values[++refs->elements] = NULL;
                }
            }
        }
        else if (!strcmp(node[i]->element, XML_TITLE)) {
            os_strdup(node[i]->content, parsed_oval->info_cves->title);
        }
        else if ((dist == FEED_UBUNTU || dist == FEED_REDHAT) && !strcmp(node[i]->element, XML_CRITERIA)) {
            if (!node[i]->attributes) {
                if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                    goto invalid_elem;
                }
                if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, condition) == OS_INVALID) {
                    retval = OS_INVALID;
                    goto end;
                }
            } else {
                char operator_found = 0;
                for (j = 0; node[i]->attributes[j]; j++) {
                    if (!strcmp(node[i]->attributes[j], XML_OPERATOR)) {
                        int result = VU_TRUE;
                        operator_found = 1;
                        if (!strcmp(node[i]->values[j], XML_OR)) {
                            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                                continue;
                            } else if (result = wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, VU_OR), result == OS_INVALID) {
                                retval = OS_INVALID;
                                goto end;
                            }

                        } else if (!strcmp(node[i]->values[j], XML_AND)) {
                            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                                continue;
                            } else if (result = wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, VU_AND), result == OS_INVALID) {
                                retval = OS_INVALID;
                                goto end;
                            }

                        } else {
                            mterror(WM_VULNDETECTOR_LOGTAG, VU_INVALID_OPERATOR, node[i]->values[j]);
                            retval = OS_INVALID;
                            goto end;
                        }
                    }
                }
                // Checks for version comparasions without operators
                if (!operator_found && node[i]->attributes && node[i]->values &&
                    *node[i]->attributes && *node[i]->values &&
                    !strcmp(*node[i]->attributes, XML_COMMENT) &&
                    !strcmp(*node[i]->values, "file version")) {
                    if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                        continue;
                    } else if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, VU_AND) == OS_INVALID) {
                        retval = OS_INVALID;
                        goto end;
                    }
                }
            }
        }
        else if ((dist == FEED_UBUNTU || dist == FEED_REDHAT) && !strcmp(node[i]->element, XML_CRITERION)) {
            for (j = 0; node[i]->attributes[j]; j++) {
                // Checks if the package isn't vulnerable
                if (!strcmp(node[i]->attributes[j], XML_COMMENT)) {
                    STATIC const char not_vulnerable[] = "while related to the CVE in some way, a decision has been made to ignore this issue";

                    if (parsed_oval->vulnerabilities->state_id && strstr(node[i]->values[j], not_vulnerable)) {
                        parsed_oval->vulnerabilities->ignore = 1; // Out of support || Will not fix
                    }
                }

                else if (!strcmp(node[i]->attributes[j], XML_TEST_REF)) {
                    if (parsed_oval->vulnerabilities->state_id &&
                        parsed_oval->vulnerabilities->cve_id) {
                        wm_vuldet_add_oval_vulnerability(parsed_oval);
                        wm_vuldet_oval_copy_rhsa(parsed_oval->vulnerabilities->prev, parsed_oval->vulnerabilities);
                        os_strdup(parsed_oval->vulnerabilities->prev->cve_id, parsed_oval->vulnerabilities->cve_id);
                    }
                    os_strdup(node[i]->values[j], parsed_oval->vulnerabilities->state_id);
                }
            }
        }
        else if (!strcmp(node[i]->element, XML_DESCRIPTION)) {
            os_strdup(node[i]->content, parsed_oval->info_cves->description);
        }
        else if (!strcmp(node[i]->element, XML_OVAL_PRODUCT_VERSION)) {
            os_strdup(node[i]->content, parsed_oval->metadata.product_version);
        }
        else if (!strcmp(node[i]->element, XML_OVAL_PRODUCT_NAME)) {
            os_strdup(node[i]->content, parsed_oval->metadata.product_name);
        }
        else if (!strcmp(node[i]->element, XML_DATE)) {
            os_strdup(node[i]->content, parsed_oval->info_cves->published);
        }
        else if (!strcmp(node[i]->element, XML_OVAL_TIMESTAMP)) {
            os_strdup(node[i]->content, parsed_oval->metadata.timestamp);
        }
        else if (!strcmp(node[i]->element, XML_OVAL_SCHEMA_VERSION)) {
            os_strdup(node[i]->content, parsed_oval->metadata.schema_version);
        }
        else if (!strcmp(node[i]->element, XML_SEVERITY)) {
            if (*node[i]->content != '\0') {
                os_strdup(node[i]->content, parsed_oval->info_cves->severity);
            } else {
                parsed_oval->info_cves->severity = NULL;
            }
        }
        else if (!strcmp(node[i]->element, XML_UPDATED)) {
            if (node[i]->attributes) {
                for (j = 0; node[i]->attributes[j]; j++) {
                    if (!strcmp(node[i]->attributes[j], XML_DATE)) {
                        os_strdup(node[i]->values[j], parsed_oval->info_cves->updated);
                    }
                }
            }
        }
        else if (dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_PUBLIC_DATE)) {
            os_strdup(node[i]->content, parsed_oval->info_cves->published);
        }
        else if (dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_BUG)) {
            if (!parsed_oval->info_cves->bugzilla_references)
                os_calloc(1, sizeof(references), parsed_oval->info_cves->bugzilla_references);

            references *bugrefs = parsed_oval->info_cves->bugzilla_references;
            os_realloc(bugrefs->values, (bugrefs->elements + 2) * sizeof(char *), bugrefs->values);
            os_strdup(node[i]->content, bugrefs->values[bugrefs->elements]);
            bugrefs->values[++bugrefs->elements] = NULL;
        }
        else if (dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_REF)) {
            if (!parsed_oval->info_cves->refs)
                os_calloc(1, sizeof(references), parsed_oval->info_cves->refs);

            references *refs = parsed_oval->info_cves->refs;
            os_realloc(refs->values, (refs->elements + 2) * sizeof(char *), refs->values);
            os_strdup(node[i]->content, refs->values[refs->elements]);
            refs->values[++refs->elements] = NULL;
        } else if (!strcmp(node[i]->element, XML_OVAL_DEFINITIONS)  ||
                   !strcmp(node[i]->element, XML_DEFINITIONS)       ||
                   !strcmp(node[i]->element, XML_OBJECTS)           ||
                   !strcmp(node[i]->element, XML_VARIABLES)         ||
                   !strcmp(node[i]->element, XML_METADATA)          ||
                   !strcmp(node[i]->element, XML_TESTS)             ||
                   !strcmp(node[i]->element, XML_STATES)            ||
                   !strcmp(node[i]->element, XML_ADVISORY)          ||
                   !strcmp(node[i]->element, XML_DEBIAN)            ||
                   !strcmp(node[i]->element, XML_GENERATOR)) {
            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                goto invalid_elem;
            } else if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, condition) == OS_INVALID) {
                retval = OS_INVALID;
                goto end;
            }
        }

        OS_ClearNode(chld_node);
        chld_node = NULL;
    }


end:
    OS_ClearNode(chld_node);
    return retval;

invalid_elem:
    mterror(WM_VULNDETECTOR_LOGTAG, XML_INVELEM, node[i]->element);
    return OS_INVALID;
}

int wm_vuldet_oval_process(update_node *update, char *path, wm_vuldet_db *parsed_vulnerabilities) {
    int success = 0;
    char *tmp_file;
    OS_XML xml;
    XML_NODE node = NULL;
    XML_NODE chld_node = NULL;

    memset(&xml, 0, sizeof(xml));

    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_PRE, vu_feed_tag[update->dist_tag_ref]);
    if (tmp_file = wm_vuldet_oval_xml_preparser(path, update->dist_ref), !tmp_file) {
        goto free_mem;
    }

    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_PAR, vu_feed_tag[update->dist_tag_ref]);
    if (OS_ReadXML(tmp_file, &xml) < 0) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_LOAD_CVE_ERROR, vu_feed_tag[update->dist_tag_ref], xml.err);
        goto free_mem;
    }

    if (node = OS_GetElementsbyNode(&xml, NULL), !node) {
        goto free_mem;
    };

    // Reduces a level of recurrence
    if (chld_node = OS_GetElementsbyNode(&xml, *node), !chld_node) {
        goto free_mem;
    }

    if (wm_vuldet_oval_xml_parser(&xml, chld_node, parsed_vulnerabilities, update, 0) == OS_INVALID) {
        goto free_mem;
    }

    success = 1;
free_mem:
    os_free(tmp_file);
    OS_ClearNode(node);
    OS_ClearNode(chld_node);
    OS_ClearXML(&xml);
    return !success;
}

int wm_vuldet_fetch_redhat(update_node *update) {
    int attempt = 1;
    int retval = VU_TRY_NEXT_PAGE;
    FILE *fp = NULL;
    char *repo = NULL;
    char buffer[OS_SIZE_128 + 1];
    int res_url_request;

    if (update->multi_url) {
        char tag[10 + 1];

        snprintf(tag, 10, "%d", update->update_it);
        repo = wstr_replace(update->multi_url, MULTI_URL_TAG, tag);
    } else {
        os_calloc(OS_SIZE_2048 + 1, sizeof(char), repo);
        snprintf(repo, OS_SIZE_2048, JSON_RED_HAT_REPO, update->update_from_year, RED_HAT_REPO_REQ_SIZE, update->update_it);
    }

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DOWNLOAD_START, repo);
    while (1) {
        res_url_request = wurl_request_uncompress_bz2_gz(repo, VU_FIT_TEMP_FILE, NULL, NULL, update->timeout, NULL);
        if (res_url_request) {
            if (attempt == RED_HAT_REPO_MAX_ATTEMPTS) {
                mtwarn(WM_VULNDETECTOR_LOGTAG, VU_API_REQ_INV_NEW, repo, RED_HAT_REPO_MAX_ATTEMPTS);
                update->update_state = VU_TRY_NEXT_PAGE;
                retval = 0;
                goto end;
            }

            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_API_REQ_INV, repo, attempt);
            attempt++;
            sleep(attempt * DOWNLOAD_SLEEP_FACTOR);
        } else {
            break;
        }
    }

    if (fp = fopen(VU_FIT_TEMP_FILE, "r"), !fp) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "fopen(%s): %s", VU_FIT_TEMP_FILE, strerror(errno));
        retval = OS_INVALID;
        update->update_state = VU_INV_FEED;
        goto end;
    }
    if (!fgets(buffer, OS_SIZE_128, fp) || !strncmp(buffer, "[]", 2)) {
        if (remove(VU_FIT_TEMP_FILE) < 0) {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_FIT_TEMP_FILE, strerror(errno));
        }
        update->update_state = VU_FINISH_FETCH;
    } else {
        update->update_state = VU_SUCC_DOWN_PAGE;
    }

    retval = 0;
end:
    free(repo);
    if (fp) {
        fclose(fp);
    }
    return retval;
}

int wm_vuldet_index_feed(update_node *update) {
    wm_vuldet_db parsed_vulnerabilities;
    const char *OS_VERSION;
    char *path;
    char success = 0;
    int compress;

    memset(&parsed_vulnerabilities, 0, sizeof(wm_vuldet_db));
    OS_VERSION = vu_feed_tag[update->dist_tag_ref];
    parsed_vulnerabilities.OS = OS_VERSION;

    if (update->json_format) {
        int result;
        // It is a feed in JSON format
        if (result = wm_vuldet_index_json(&parsed_vulnerabilities, update,
                                update->multi_path ? update->multi_path : VU_FIT_TEMP_FILE,
                                update->multi_path ? 1 : 0), result == OS_INVALID) {
            goto free_mem;
        } else if (result == VU_NOT_NEED_UPDATE) {
            success = 1;
            goto free_mem;
        }
    } else {
        path = update->path ? update->path : VU_TEMP_FILE;

        if (update->dist_ref == FEED_UBUNTU ||
            update->dist_ref == FEED_DEBIAN ||
            update->dist_ref == FEED_REDHAT) {

            switch (w_uncompress_bz2_gz_file(path, VU_TEMP_FILE)) {
            case -1:
                mterror(WM_VULNDETECTOR_LOGTAG, VU_CONTENT_FEED_ERROR, update->dist_ext, path);
                goto free_mem;
            case 0:
                compress = 1;
                break;
            default:
                compress = 0;
            }

            if (wm_vuldet_oval_process(update, compress ? VU_TEMP_FILE : path, &parsed_vulnerabilities)) {
                goto free_mem;
            }
        }
        else if (update->dist_ref == FEED_CPED) {
            if (wm_vuldet_nvd_cpe_parser(path, &parsed_vulnerabilities)) {
                goto free_mem;
            }
        }
    }

    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_START_REFRESH_DB, update->dist_ext);

    if (wm_vuldet_insert(&parsed_vulnerabilities, update)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_REFRESH_DB_ERROR, OS_VERSION);
        goto free_mem;
    }
    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_STOP_REFRESH_DB, update->dist_ext);

    success = 1;
free_mem:
    if (remove(VU_TEMP_FILE) < 0) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_TEMP_FILE, strerror(errno));
    }
    if (remove(VU_FIT_TEMP_FILE) < 0) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_FIT_TEMP_FILE, strerror(errno));
    }
    if (remove(VU_TEMP_FILE_BZ2) < 0) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_TEMP_FILE_BZ2, strerror(errno));
    }

    if (success) {
        return 0;
    }

    return OS_INVALID;
}

int wm_vuldet_fetch_oval(update_node *update, char *repo) {
    STATIC const char *timestamp_tag = "timestamp>";
    char timestamp[OS_SIZE_256 + 1] = {0};
    char buffer[OS_MAXSTR + 1] = {0};
    FILE *fp = NULL;
    char *found;
    int attempts;
    vu_logic retval = VU_INV_FEED;

    for (attempts = 0;; attempts++) {
        int res_url_request = wurl_request_uncompress_bz2_gz(repo, VU_TEMP_FILE, NULL, NULL, update->timeout, NULL);
        if (!res_url_request) {
            break;
        } else if (attempts == WM_VULNDETECTOR_DOWN_ATTEMPTS) {
            goto end;
        }
        mdebug1(VU_DOWNLOAD_FAIL, attempts);
        sleep(attempts);
    }

    if (fp = fopen(VU_TEMP_FILE, "r"), !fp) {
        goto end;
    }

    while (fgets(buffer, OS_MAXSTR, fp)) {
        if (found = strstr(buffer, timestamp_tag), found) {
            char *close_tag;
            found+=strlen(timestamp_tag);

            if (close_tag = strstr(found, "<"), !close_tag) {
                goto end;
            }
            *close_tag = '\0';

            switch (wm_vuldet_check_timestamp(vu_feed_tag[update->dist_tag_ref], found, timestamp)) {
                case VU_TIMESTAMP_FAIL:
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED_ERROR, update->dist_ext);
                    goto end;
                break;
                case VU_TIMESTAMP_UPDATED:
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_DATE, update->dist_ext);
                    retval = VU_NOT_NEED_UPDATE;
                    goto end;
                break;
                case VU_TIMESTAMP_OUTDATED:
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED, update->dist_ext, "");
                break;
            }
            break;
        }
    }

    retval = VU_NEED_UPDATE;
end:
    if (fp) {
        w_fclose(fp);
    }

    return retval;
}

bool wm_vuldet_set_feed_update_url(update_node *update) {
    char *repo = NULL;
    char *low_repo = NULL;

    // Discard non-harcoded URLs and expected feeds
    if (update->url ||
        update->dist_ref == FEED_CPED    ||
        update->dist_ref == FEED_NVD     ||
        update->dist_ref == FEED_CPEW    ||
        update->dist_ref == FEED_JREDHAT ||
        update->dist_ref == FEED_ARCH    ||
        update->dist_ref == FEED_MSU) {
            return true;
    }

    os_calloc(1, OS_SIZE_2048 + 1, repo);

    // Ubuntu and debian build their repos in a specific way
    if (update->dist_ref == FEED_UBUNTU) {
        os_strdup(update->version, low_repo);
        str_lowercase(low_repo);
        snprintf(repo, OS_SIZE_2048, CANONICAL_REPO, low_repo);
        free(low_repo);

    } else if (update->dist_ref == FEED_DEBIAN) {
        os_strdup(update->version, low_repo);
        str_lowercase(low_repo);
        snprintf(repo, OS_SIZE_2048, DEBIAN_REPO, low_repo);
        free(low_repo);

    } else if (update->dist_ref == FEED_REDHAT) {
        if (update->dist_tag_ref == FEED_RHEL5)
            snprintf(repo, OS_SIZE_2048, "%s", RED_HAT5_REPO);
        else // RHEL6, RHEL7 and RHEL8
            snprintf(repo, OS_SIZE_2048, RED_HAT_REPO, update->version, update->version);

    } else {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_OS_VERSION_ERROR);
        os_free(repo);
        return false;
    }

    update->url = repo;

    return true;
}

int wm_vuldet_check_timestamp(const char *target, char *timst, char *ret_timst) {
    int retval = VU_TIMESTAMP_FAIL;
    const char *stored_timestamp;
    sqlite3_stmt *stmt = NULL;
    sqlite3 *db = NULL;

    if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
        goto end;
    } else {
        if (wm_vuldet_prepare(db, vu_queries[TIMESTAMP_QUERY], -1, &stmt, NULL) != SQLITE_OK) {
            goto end;
        }
        sqlite3_bind_text(stmt, 1, target, -1, NULL);
        if (wm_vuldet_step(stmt) == SQLITE_ROW) {
            if (stored_timestamp = (const char *) sqlite3_column_text(stmt, 0), stored_timestamp) {
                if (!strcmp(stored_timestamp, timst)) {
                    retval = VU_TIMESTAMP_UPDATED;
                    if (ret_timst) {
                        snprintf(ret_timst, OS_SIZE_256, "%s", stored_timestamp);
                    }
                    goto end;
                }
            }
        }
        retval = VU_TIMESTAMP_OUTDATED;
    }
end:
    if (db) {
        sqlite3_close_v2(db);
    }
    wdb_finalize(stmt);
    return retval;
}

char * wm_vuldet_get_hash(const char *target) {
    sqlite3_stmt *stmt = NULL;
    sqlite3 *db = NULL;

    if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
        wm_vuldet_sql_error(db, stmt);
        return NULL;
    }

    if (wm_vuldet_prepare(db, vu_queries[HASH_QUERY], -1, &stmt, NULL) != SQLITE_OK) {
        wm_vuldet_sql_error(db, stmt);
        return NULL;
    }

    char *hash = NULL;

    sqlite3_bind_text(stmt, 1, target, -1, NULL);
    if (wm_vuldet_step(stmt) == SQLITE_ROW) {
        char *val = (char*)sqlite3_column_text(stmt, 0);
        sqlite_strdup((const char *) val, hash);
    }

    sqlite3_close_v2(db);
    wdb_finalize(stmt);

    return hash;
}

int wm_vuldet_fetch_feed(update_node *update, int8_t *need_update) {
    char repo[OS_SIZE_2048 + 1] = {'\0'};
    unsigned char success = 0;
    int result;
    *need_update = 1;

    // Clean metadata when set a custom location
    if (update->custom_location) {

        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_METADATA_CLEAN, vu_feed_tag[update->dist_tag_ref]);

        sqlite3 *db;
        if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, NULL);
        }

        if (wm_vuldet_remove_target_table(db, METADATA_TABLE, vu_feed_tag[update->dist_tag_ref])) {
            return OS_INVALID;
        }

        sqlite3_close_v2(db);
    }

    if (!update->url && (update->path || (update->multi_path && update->dist_ref != FEED_DEBIAN))) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_LOCAL_FETCH, update->path ? update->path : update->multi_path);
        return 0;
    }

    // Only for those O.S with more than one feed.
    if (wm_vuldet_set_feed_update_url(update) == false && !update->url) {
        return OS_INVALID;
    } else if (update->url != NULL) { // For tags <os url=''></os> and (msu)<url></url>
        snprintf(repo, OS_SIZE_2048, "%s", update->url);
    }

    // Call to the specific fetch function
    switch (update->dist_ref) {
        case FEED_JREDHAT:
            update->update_state = 0;
            if (result = wm_vuldet_fetch_redhat(update), result != OS_INVALID) {
                success = 1;
                if (update->update_state == VU_FINISH_FETCH || update->update_state == VU_TRY_NEXT_PAGE || update->update_state == VU_INV_FEED) {
                    *need_update = 0;
                }
            }

            goto end;

        case FEED_ARCH:
            if (result = wm_vuldet_fetch_arch(update, repo), result == VU_INV_FEED) {
                goto end;
            } else if (result == VU_NOT_NEED_UPDATE) {
                *need_update = 0;
            }
        break;

        case FEED_CPED:
            if (wm_vuldet_fetch_nvd_cpe(update->timeout, repo)) {
                goto end;
            }
        break;

        case FEED_NVD:
            if (result = wm_vuldet_fetch_nvd_cve(update), result == VU_INV_FEED) {
                goto end;
            } else if (result == VU_NOT_NEED_UPDATE) {
                *need_update = 0;
            }
        break;

        case FEED_CPEW:
            if (result = wm_vuldet_fetch_wazuh_cpe(update), result == VU_INV_FEED) {
                goto end;
            } else if (result == VU_NOT_NEED_UPDATE) {
                *need_update = 0;
            }
        break;

        case FEED_MSU:
            if (result = wm_vuldet_fetch_MSU(update, repo), result == VU_INV_FEED) {
                goto end;
            } else if (result == VU_NOT_NEED_UPDATE) {
                *need_update = 0;
            }
        break;

        default:
            if (result = wm_vuldet_fetch_oval(update, repo), result == VU_INV_FEED) {
                goto end;
            } else if (result == VU_NOT_NEED_UPDATE) {
                *need_update = 0;
            }
    }

    success = 1;
end:
    if (success) {
        return 0;
    } else {
        mtwarn(WM_VULNDETECTOR_LOGTAG, VU_FETCH_ERROR, update->dist_ext);
        return OS_INVALID;
    }
}

int wm_vuldet_check_feed(update_node *upd, int8_t *updated) {
    if (wm_vuldet_check_update_period(upd)) {
        if (!wm_vuldet_silent_feed(upd->dist_tag_ref)) {
            mtinfo(WM_VULNDETECTOR_LOGTAG, VU_STARTING_UPDATE, vu_feed_ext[upd->dist_tag_ref]);
        } else {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_STARTING_UPDATE, vu_feed_ext[upd->dist_tag_ref]);
        }
        if (wm_vuldet_update_feed(upd, updated)) {
            if (!upd->attempted) {
                upd->last_update = time(NULL) - upd->interval + WM_VULNDETECTOR_RETRY_UPDATE;
                upd->attempted = 1;
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_RETRY, upd->dist, upd->version ? upd->version : "provider", (long unsigned)WM_VULNDETECTOR_RETRY_UPDATE);
            } else {
                upd->last_update = time(NULL);
                upd->attempted = 0;
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_RETRY, upd->dist, upd->version ? upd->version : "provider", upd->interval);
            }

            if (upd->dist_tag_ref == FEED_CPEW) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_NVD_UPD_CANCEL, "Wazuh CPE Helper");
                *updated = -1;
            }

            return OS_INVALID;
        } else {
            if (!wm_vuldet_silent_feed(upd->dist_tag_ref)) {
                mtinfo(WM_VULNDETECTOR_LOGTAG, VU_ENDING_UPDATE, upd->dist_ext);
            } else {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_STARTING_UPDATE, vu_feed_ext[upd->dist_tag_ref]);
            }
            upd->last_update = time(NULL);
        }
    }
    return 0;
}

cJSON *wm_vuldet_get_debian_status_feed(update_node *update) {
    FILE *deb_file = NULL;
    int attempts;
    int res_url_request;
    char * path = NULL;
    char * url = DEBIAN_REPO_STATUS;
    cJSON * json_file = NULL;

    if (update->multi_path) {
        path = update->multi_path;
    }

    if (update->multi_url) {
        url = update->multi_url;
    }

    if (path) {
        // Fetch Debian Security Tracker feed locally
        if (w_is_file(path)) {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_DEB_STATUS_FETCH, path);
            json_file = wm_vuldet_json_fread(path);
            return json_file;
        } else {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, "Unable to read file '%s'", path);
        }
    }

    if (deb_file = fopen(VU_DEB_TEMP_FILE, "r"), !deb_file) {
        // Download Debian status feed
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_DEB_STATUS_FETCH, url);
        for (attempts = 0;; attempts++) {
            res_url_request = wurl_request_uncompress_bz2_gz(url, VU_DEB_TEMP_FILE, NULL, NULL, update->timeout, NULL);
            if (!res_url_request) {
                break;
            } else if (attempts == WM_VULNDETECTOR_DOWN_ATTEMPTS) {
                return NULL;
            }
            mdebug1(VU_DOWNLOAD_FAIL, attempts);
            sleep(attempts);
        }
    } else {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_DEB_STATUS_FETCH, VU_DEB_TEMP_FILE);
        fclose(deb_file);
    }

    // Parse Debian status feed
    return wm_vuldet_json_fread(VU_DEB_TEMP_FILE);
}

int wm_vuldet_run_update(update_node **updates) {
    int ret = 0;
    int8_t updated = 1;
    int up_status = 0;

    // Check updates for all the feeds
    for (int os = 0; os < OS_SUPP_SIZE; os++) {
        if (!updates[os])
            continue;

        // If none of RedHat's OVALs have been updated,
        // we can deduce that the JSON is already up to date.
        if (updates[os]->dist_ref == FEED_JREDHAT && up_status == 1) {
            updates[os]->last_update = time(NULL);
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_DATE, vu_feed_ext[FEED_JREDHAT]);
            continue;
        }

        if (wm_vuldet_check_feed(updates[os], &updated)) {
            if (updated == -1) {
                // The CPE helper update failed
                wm_vuldet_release_update_node(updates, CPE_WDIC);
                wm_vuldet_release_update_node(updates, CVE_NVD);
            }
            ret = OS_INVALID;
        }

        /** @updated -> If the RHEL feed was successfully updated(1).
        * +1 Represents the availability of the RHEL feeds. Otherwise we
        * won't be capable of distinguishing between non available RHEL feeds, and
        * available but up-to-date feeds. (In both cases @up_status would be 0).
        **/
        up_status |= (updates[os]->dist_ref == FEED_REDHAT)? updated + 1 : up_status;
    }

    // Remove Debian status feed
    if (remove(VU_DEB_TEMP_FILE) < 0 && errno != ENOENT) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_DEB_TEMP_FILE, strerror(errno));
    }

    return ret;
}

int wm_vuldet_json_rh_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities) {
    cJSON *json_it;
    cJSON *cve_content;
    time_t l_time;
    struct tm *tm_time;

    static char *JSON_CVE = "CVE";
    static char *JSON_SEVERITY = "severity";
    static char *JSON_PUBLIC_DATE = "public_date";
    static char *JSON_ADVISORIES = "advisories";
    static char *JSON_BUGZILLA = "bugzilla";
    static char *JSON_BUGZILLA_DESCRIPTION = "bugzilla_description";
    static char *JSON_CVSS_SCORE = "cvss_score";
    static char *JSON_CVSS_SCORING_VECTOR = "cvss_scoring_vector";
    static char *JSON_CWE = "CWE";
    static char *JSON_AFFECTED_PACKAGES = "affected_packages";
    static char *JSON_RESOURCE_URL = "resource_url";
    static char *JSON_CVSS3_SCORE = "cvss3_score";
    static char *JSON_CVSS3_SCORING_VECTOR = "cvss3_scoring_vector";

    // Metadata values
    STATIC char *rh_product_name = "Red Hat Security Data";
    STATIC char *rh_product_version = "1.0";
    char *m_product_name = NULL;
    char *m_product_version = NULL;
    char *m_schema_version = NULL;
    char m_timestamp[27] = { '\0' };

    for (json_it  = json_feed->child; json_it; json_it = json_it->next) {
        char *tmp_cve = NULL;
        char *tmp_severity = NULL;
        char *tmp_public_date = NULL;
        cJSON *tmp_advisories = NULL;
        char *tmp_bugzilla = NULL;
        char *tmp_bugzilla_description = NULL;
        double tmp_cvss_score = -1;
        char *tmp_cvss_scoring_vector = NULL;
        char *tmp_cwe = NULL;
        double tmp_cvss3_score = -1;
        char *tmp_cvss3_scoring_vector = NULL;

        time(&l_time);
        tm_time = localtime(&l_time);
        strftime(m_timestamp, 26, "%Y-%m-%d %H:%M:%S", tm_time);
        m_product_name = rh_product_name;
        m_product_version = rh_product_version;

        for (cve_content  = json_it->child; json_tagged_obj(cve_content); cve_content = cve_content->next) {
            if (!strcmp(cve_content->string, JSON_CVE)) {
                tmp_cve = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_SEVERITY)) {
                tmp_severity = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_PUBLIC_DATE)) {
                tmp_public_date = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_ADVISORIES)) {
                tmp_advisories = cve_content->child;
            } else if (!strcmp(cve_content->string, JSON_BUGZILLA)) {
                tmp_bugzilla = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_BUGZILLA_DESCRIPTION)) {
                tmp_bugzilla_description = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_CVSS_SCORE)) {
                if (cve_content->type == cJSON_String) {
                    tmp_cvss_score = atof(cve_content->valuestring);
                } else if (cve_content->type == cJSON_Number) {
                    tmp_cvss_score = cve_content->valuedouble;
                }
            } else if (!strcmp(cve_content->string, JSON_CVSS_SCORING_VECTOR)) {
                tmp_cvss_scoring_vector = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_CWE)) {
                tmp_cwe = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_RESOURCE_URL)) {
            } else if (!strcmp(cve_content->string, JSON_CVSS3_SCORE)) {
                if (cve_content->type == cJSON_String) {
                    tmp_cvss3_score = atof(cve_content->valuestring);
                } else if (cve_content->type == cJSON_Number) {
                    tmp_cvss3_score = cve_content->valuedouble;
                }
            } else if (!strcmp(cve_content->string, JSON_CVSS3_SCORING_VECTOR)) {
                tmp_cvss3_scoring_vector = cve_content->valuestring;
            } else if (strcmp(cve_content->string, JSON_AFFECTED_PACKAGES)) {
                // Discard all the "affected_packages" tags.
                mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UNEXP_JSON_KEY, cve_content->string);
            }
        }

        if(!tmp_bugzilla_description || !tmp_cve) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FEED_NODE_NULL_ELM);
            return OS_INVALID;
        }

        wm_vuldet_adapt_title(tmp_bugzilla_description, tmp_cve);

        // Fill in vulnerability information
        wm_vuldet_add_vulnerability_info(parsed_vulnerabilities);
        w_strdup(tmp_cve, parsed_vulnerabilities->info_cves->cveid);
        w_strdup(tmp_severity, parsed_vulnerabilities->info_cves->severity);
        w_strdup(tmp_public_date, parsed_vulnerabilities->info_cves->published);

        // Setting the reference. In RH we only have one reference link to save in the array for each CVE
        if (!parsed_vulnerabilities->info_cves->refs)
            os_calloc(1, sizeof(references), parsed_vulnerabilities->info_cves->refs);

        references *refs = parsed_vulnerabilities->info_cves->refs;
        os_realloc(refs->values, (refs->elements + 2) * sizeof(char *), refs->values);
        refs->values[refs->elements] = wm_vuldet_build_url(VU_BUILD_REF_CVE_RH, tmp_cve);
        refs->values[++refs->elements] = NULL;

        w_strdup(tmp_bugzilla_description, parsed_vulnerabilities->info_cves->description);
        parsed_vulnerabilities->info_cves->cvss = (tmp_cvss_score != -1) ? w_double_str(tmp_cvss_score) : NULL;
        parsed_vulnerabilities->info_cves->cvss3 = (tmp_cvss3_score != -1) ? w_double_str(tmp_cvss3_score) : NULL;
        w_strdup(tmp_cvss_scoring_vector, parsed_vulnerabilities->info_cves->cvss_vector);
        w_strdup(tmp_cvss3_scoring_vector, parsed_vulnerabilities->info_cves->cvss3_vector);

        // Setting the bugzilla reference. In RH we only have one bugzilla reference link to save in the array for each CVE
        if (!parsed_vulnerabilities->info_cves->bugzilla_references)
            os_calloc(1, sizeof(references), parsed_vulnerabilities->info_cves->bugzilla_references);

        references *bugrefs = parsed_vulnerabilities->info_cves->bugzilla_references;
        os_realloc(bugrefs->values, (bugrefs->elements + 2) * sizeof(char *), bugrefs->values);
        bugrefs->values[bugrefs->elements] = wm_vuldet_build_url(VU_BUILD_REF_BUGZ, tmp_bugzilla);
        bugrefs->values[++bugrefs->elements] = NULL;

        parsed_vulnerabilities->info_cves->advisories = wm_vuldet_extract_advisories(tmp_advisories);
        w_strdup(tmp_cwe, parsed_vulnerabilities->info_cves->cwe);
    }

    // Insert metadata values
    os_free(parsed_vulnerabilities->metadata.product_name);
    w_strdup(m_product_name, parsed_vulnerabilities->metadata.product_name);
    os_free(parsed_vulnerabilities->metadata.product_version);
    w_strdup(m_product_version, parsed_vulnerabilities->metadata.product_version);
    os_free(parsed_vulnerabilities->metadata.schema_version);
    w_strdup(m_schema_version, parsed_vulnerabilities->metadata.schema_version);
    os_free(parsed_vulnerabilities->metadata.timestamp);
    os_strdup(m_timestamp, parsed_vulnerabilities->metadata.timestamp);

    return 0;
}

int wm_vuldet_json_arch_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities) {
    cJSON *json_it;
    cJSON *cve_content;
    cJSON *packages_content;
    time_t l_time;
    struct tm *tm_time;

    static char *JSON_NAME = "name";
    static char *JSON_PACKAGES = "packages";
    static char *JSON_STATUS = "status";
    static char *JSON_SEVERITY = "severity";
    static char *JSON_AFFECTED = "affected";
    static char *JSON_FIXED = "fixed";
    static char *JSON_ISSUES = "issues";
    static char *JSON_ADVISORIES = "advisories";

    // Metadata values
    STATIC char *arch_product_name = "Arch Linux Security Data";
    STATIC char *arch_product_version = "1.0";
    char *m_product_name = NULL;
    char *m_product_version = NULL;
    char *m_schema_version = NULL;
    char m_timestamp[27] = { '\0' };

    for (json_it  = json_feed->child; json_it; json_it = json_it->next) {
        char *tmp_name = NULL;
        cJSON *tmp_packages = NULL;
        char *tmp_status = NULL;
        char *tmp_severity = NULL;
        char *tmp_affected = NULL;
        char *tmp_fixed = NULL;
        cJSON *tmp_issues = NULL;
        cJSON *tmp_advisories = NULL;

        time(&l_time);
        tm_time = localtime(&l_time);
        strftime(m_timestamp, 26, "%Y-%m-%d %H:%M:%S", tm_time);
        m_product_name = arch_product_name;
        m_product_version = arch_product_version;

        for (cve_content = json_it->child; json_tagged_obj(cve_content); cve_content = cve_content->next) {
            if (!strcmp(cve_content->string, JSON_NAME)) {
                tmp_name = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_PACKAGES)) {
                tmp_packages = cve_content->child;
            } else if (!strcmp(cve_content->string, JSON_STATUS)) {
                tmp_status = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_SEVERITY)) {
                tmp_severity = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_AFFECTED)) {
                tmp_affected = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_FIXED)) {
                tmp_fixed = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_ISSUES)) {
                tmp_issues = cve_content->child;
            } else if (!strcmp(cve_content->string, JSON_ADVISORIES)) {
                tmp_advisories = cve_content->child;
            }
        }

        if (!tmp_name || !tmp_severity || !tmp_packages || !tmp_affected || !tmp_status) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FEED_NODE_NULL_ELM);
            return OS_INVALID;
        }

        char first_cve = 1;
        for (cve_content = tmp_issues; first_cve || cve_content; cve_content = cve_content ? cve_content->next : NULL) {
            first_cve = 0;

            for (packages_content = tmp_packages; packages_content; packages_content = packages_content->next) {
                wm_vuldet_add_vulnerability_info(parsed_vulnerabilities);
                wm_vuldet_add_oval_vulnerability(parsed_vulnerabilities);

                // Fill in vulnerability information
                if (cve_content) {
                    w_strdup(cve_content->valuestring, parsed_vulnerabilities->info_cves->cveid);
                    w_strdup(cve_content->valuestring, parsed_vulnerabilities->vulnerabilities->cve_id);
                } else {
                    w_strdup("Unspecified", parsed_vulnerabilities->info_cves->cveid);
                    w_strdup("Unspecified", parsed_vulnerabilities->vulnerabilities->cve_id);
                }

                w_strdup(tmp_severity, parsed_vulnerabilities->info_cves->severity);

                // Setting the reference. In Arch we only have one reference link to save in the array for each CVE
                if (!parsed_vulnerabilities->info_cves->refs)
                    os_calloc(1, sizeof(references), parsed_vulnerabilities->info_cves->refs);

                references *refs = parsed_vulnerabilities->info_cves->refs;
                os_realloc(refs->values, (refs->elements + 2) * sizeof(char *), refs->values);
                refs->values[refs->elements] = wm_vuldet_build_url(VU_BUILD_REF_CVE_ARCH, tmp_name);
                refs->values[++refs->elements] = NULL;

                if (!parsed_vulnerabilities->info_cves->bugzilla_references)
                    os_calloc(1, sizeof(references), parsed_vulnerabilities->info_cves->bugzilla_references);

                parsed_vulnerabilities->info_cves->advisories = wm_vuldet_extract_advisories(tmp_advisories);
                os_strdup(packages_content->valuestring, parsed_vulnerabilities->vulnerabilities->package_name);
                if (tmp_fixed && (!strcmp(tmp_status, "Fixed"))) {
                    os_strdup(tmp_fixed, parsed_vulnerabilities->vulnerabilities->package_version);
                    os_strdup("less than", parsed_vulnerabilities->vulnerabilities->state_id);
                } else if (!strcmp(tmp_status, "Vulnerable")) {
                    os_strdup(tmp_affected, parsed_vulnerabilities->vulnerabilities->package_version);
                    os_strdup("greater than or equal", parsed_vulnerabilities->vulnerabilities->state_id);
                }
            }
        }
    }

    // Insert metadata values
    os_free(parsed_vulnerabilities->metadata.product_name);
    w_strdup(m_product_name, parsed_vulnerabilities->metadata.product_name);
    os_free(parsed_vulnerabilities->metadata.product_version);
    w_strdup(m_product_version, parsed_vulnerabilities->metadata.product_version);
    os_free(parsed_vulnerabilities->metadata.schema_version);
    w_strdup(m_schema_version, parsed_vulnerabilities->metadata.schema_version);
    os_free(parsed_vulnerabilities->metadata.timestamp);
    os_strdup(m_timestamp, parsed_vulnerabilities->metadata.timestamp);

    return 0;
}

int wm_vuldet_json_parser(char *json_path, wm_vuldet_db *parsed_vulnerabilities, update_node *update) {
    int retval = OS_INVALID;
    int compress = 0;

    switch (w_uncompress_bz2_gz_file(json_path, VU_FIT_TEMP_FILE)) {
    case -1:
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CONTENT_FEED_ERROR, update->dist_ext, json_path);
        return retval;
    case 0:
        compress = 1;
        break;
    default:
        compress = 0;
    }

    if (update->dist_ref == FEED_NVD) {
        char *json_feed;
        if (json_feed = w_get_file_content(compress ? VU_FIT_TEMP_FILE : json_path, JSON_MAX_FSIZE), !json_feed) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_CONTENT_FEED_ERROR, update->dist_ext, json_path);
            return retval;
        }

        if (retval = wm_vuldet_json_nvd_parser(json_feed, parsed_vulnerabilities), retval == OS_INVALID) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_PARSED_FEED_ERROR, update->dist_ext, json_path);
        }
        free(json_feed);
    }
    else {
        cJSON *json_feed;

        if (json_feed = wm_vuldet_json_fread(compress ? VU_FIT_TEMP_FILE : json_path), !json_feed) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_PARSED_FEED_ERROR, update->dist_ext, json_path);
            return retval;
        }

        switch ((int) update->dist_ref) {
            case FEED_JREDHAT:
                retval = wm_vuldet_json_rh_parser(json_feed, parsed_vulnerabilities);
                break;
            case FEED_ARCH:
                retval = wm_vuldet_json_arch_parser(json_feed, parsed_vulnerabilities);
                break;
            case FEED_CPEW:
                retval = wm_vuldet_json_wcpe_parser(json_feed, parsed_vulnerabilities);
                break;
            case FEED_MSU:
                retval = wm_vuldet_json_msu_parser(json_feed, parsed_vulnerabilities);
                break;
            default:
                break;
        }
        cJSON_Delete(json_feed);
    }

    return retval;
}

int wm_vuldet_get_software_info(agent_software *agent, sqlite3 *db, time_t ignore_time, int* request) {
    unsigned int i;
    int size;
    char buffer[OS_SIZE_6144];
    char json_str[OS_SIZE_6144 + 10];
    char scan_id[OS_SIZE_128];
    int retval = OS_INVALID;
    cJSON *obj = NULL;
    cJSON *package_list = NULL;
    int min_cpe_index;
    cpe_list *node_list = NULL;
    const char *jsonErrPtr;
    char hotfix_scan = 0;

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AGENT_SOFTWARE_REQ, atoi(agent->agent_id));

    // Check to see if the scan has already been reported
    *request = wm_vuldet_select_scan_type(agent->agent_id, ignore_time);
    switch (*request) {
        case OS_INVALID:
            goto end;
        case VU_SOFTWARE_FULL_REQ:
            if (agent->dist == FEED_WIN && wm_vuldet_clean_agent_cpes(agent->agent_id)) {
                goto end;
            }
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AG_FULL_SCAN, atoi(agent->agent_id));
            wm_vuldet_update_last_scan(agent->agent_id);
            if (agent->dist == FEED_WIN) {
                hotfix_scan = 1;
            }
            break;
        case VU_SOFTWARE_REQUEST:
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AG_PART_SCAN, atoi(agent->agent_id));
    }

    // Get the last sys_program scan ID
    switch (wm_vuldet_get_last_software_scan(agent->agent_id, scan_id)) {
        case 1: // No sys_program scan
            // We will not continue scanning unless it is a
            // Windows agent and hotfix scanning is enabled
            if (!hotfix_scan) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_NO_SYSC_SCANS, atoi(agent->agent_id));
                retval = 2;
                goto end;
            } else {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_NO_PACKAGE_SCAN, atoi(agent->agent_id));
            }
        case 0:
            break;
        case OS_INVALID:
            goto end;
        }

    // Request and store packages
    i = 0;
    snprintf(buffer, OS_SIZE_6144, vu_queries[*request], agent->agent_id, scan_id, VU_MAX_PACK_REQ, i);
    if (wm_vuldet_send_wdb(buffer)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_SOFTWARE_REQUEST_ERROR, atoi(agent->agent_id));
        goto end;
    }

    while (size = wm_vuldet_recv_wdb(buffer, OS_SIZE_6144), size != OS_INVALID) {
        if (size > 0) {
            if (size < 10) {
                break;
            }
            if (wm_vuldet_wdb_valid_answ(buffer)) {
                buffer[0] = buffer[1] = ' ';
                snprintf(json_str, OS_SIZE_6144 + 10, "{\"data\":%s}", buffer);
            } else {
                goto end;
            }
            if (obj) {
                cJSON *new_obj;
                cJSON *data;
                if (new_obj = cJSON_ParseWithOpts(json_str, &jsonErrPtr, 0), !new_obj) {
                    goto end;
                } else if (!cJSON_IsObject(new_obj)) {
                    free(new_obj);
                    goto end;
                }
                data = cJSON_GetObjectItem(new_obj, "data");
                if (data) {
                    cJSON_AddItemToArray(package_list, data->child);
                    free(data->string);
                    free(data);
                }
                free(new_obj);
            } else if (obj = cJSON_ParseWithOpts(json_str, &jsonErrPtr, 0), obj && cJSON_IsObject(obj)) {
                package_list = cJSON_GetObjectItem(obj, "data");
                if (!package_list) {
                    goto end;
                }
            } else {
                goto end;
            }

            i += VU_MAX_PACK_REQ;
            snprintf(buffer, OS_SIZE_6144, vu_queries[*request], agent->agent_id, scan_id, VU_MAX_PACK_REQ, i);
            if (wm_vuldet_send_wdb(buffer)) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_SOFTWARE_REQUEST_ERROR, atoi(agent->agent_id));
                goto end;
            }
        } else {
            goto end;
        }
    }

    // Avoid checking the same packages again
    snprintf(buffer, OS_SIZE_6144, vu_queries[VU_SYSC_UPDATE_SCAN], agent->agent_id, scan_id);
    if (wm_vuldet_wdb_request(buffer, OS_SIZE_6144)) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_SOFTWARE_REQUEST_ERROR, atoi(agent->agent_id));
        goto end;
    }

    if (wm_vuldet_get_min_cpe_index(db, &min_cpe_index)) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_CPE_INDEX_GET_ERROR);
        goto end;
    }

    if (hotfix_scan) {
        // Request the agent hotfixes
        int result;
        if (result = wm_vuldet_request_hotfixes(db, agent->agent_id), result == OS_INVALID) {            
            mterror(WM_VULNDETECTOR_LOGTAG, VU_HOTFIX_REQUEST_ERROR, atoi(agent->agent_id));
            goto end;
        }

        if (wm_vuldet_check_enabled_msu(db) == false) {
            mtwarn(WM_VULNDETECTOR_LOGTAG, VU_NO_HOTFIX_DISABLED, atoi(agent->agent_id));

        } else {
            // Get the os info
            if (wm_vuldet_get_oswindows_info(agent)) {
                goto end;
            }

            // Generate the OS CPE
            if (wm_vuldet_generate_os_cpe(db, agent, &min_cpe_index, &node_list)) {
                goto end;
            }
            agent->info = 1;
        }
    }

    if (agent->dist != FEED_WIN) {
        // Get the os info
        if (wm_vuldet_get_os_unix_info(agent)) {
            goto end;
        }

        // Generate the OS CPE
        if (wm_vuldet_generate_os_and_kernel_package(db, agent)) {
            goto end;
        }
        agent->info = 1;
    }

    if (package_list) {
        sqlite3_exec(db, vu_queries[BEGIN_T], NULL, NULL, NULL);

        for (package_list = package_list->child; package_list; package_list = package_list->next) {
            char *name = (char *) wm_vuldet_get_json_value(package_list, "name");
            char *version = (char *) wm_vuldet_get_json_value(package_list, "version");
            char *architecture = (char *) wm_vuldet_get_json_value(package_list, "architecture");
            char *cpe_str = (char *) wm_vuldet_get_json_value(package_list, "cpe");
            char *vendor = (char *) wm_vuldet_get_json_value(package_list, "vendor");
            char *msu_name = (char *) wm_vuldet_get_json_value(package_list, "msu_name");
            char *source = (char *) wm_vuldet_get_json_value(package_list, "source");
            char *src_version = NULL;

            if (!architecture  && agent->dist == FEED_MAC) {
                architecture = agent->arch;
            }

            if (name && version && architecture) {
                struct cpe *s_cpe = NULL;
                char success = 1;

                if (cpe_str) {
                    s_cpe = wm_vuldet_decode_cpe(cpe_str);
                    if (s_cpe) {
                        w_strdup(msu_name, s_cpe->msu_name);
                    }
                }

                if (agent->dist != FEED_WIN && agent->dist != FEED_MAC) {
                    if (wm_vuldet_discard_kernel_package(agent, name, version, architecture)) {
                        continue;
                    }
                }

                if (source) {
                    if (agent->dist == FEED_REDHAT || agent->dist == FEED_MAC) {
                        source = NULL;
                    } else {
                        // Separate version from source
                        char *source_end = strstr(source, " ");
                        char *source_version_start;
                        char *source_version_end;
                        if (source_end) {
                            source_version_start = strstr(source_end, "(");
                            if (source_version_start) {
                                source_version_end = strstr(source_version_start, ")");
                                if (source_version_end) {
                                    src_version = source_version_start + 1;
                                    *source_version_end = '\0';
                                }
                            }
                            *source_end = '\0';
                        }
                    }
                }

                if (agent->dist == FEED_MAC) {
                    wchr_replace(name, ' ', '_');
                    if (vendor) wchr_replace(vendor, ' ', '_');
                }

                // Generate the node_list structure with those packages
                // that have associated CPE
                if (wm_vuldet_insert_agent_data(db, agent, &min_cpe_index, vendor, name,
                                                source, version, src_version, architecture, s_cpe, &node_list)) {
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_INSERT_DATA_ERR,
                            cpe_str ? cpe_str : name);
                    success = 0;
                }

                if (s_cpe) {
                    wm_vuldet_free_cpe(s_cpe);
                }

                if (!success) {
                    goto end;
                }
            }
        }
        sqlite3_exec(db, vu_queries[END_T], NULL, NULL, NULL);
        agent->info = 1;
    } else if (*request == VU_SOFTWARE_REQUEST) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_NO_SOFTWARE, atoi(agent->agent_id));
        if (agent->dist != FEED_WIN) {
            // If no new inventory is available, skip the agent
            // Only for Linux agents, on Windows the hotfixes scan may be enabled
            retval = 2;
            goto end;
        }
    }

    // Generate the agent's CPEs
    // Only for Windows agents
    if (agent->dist == FEED_WIN) {
        sqlite3_exec(db, vu_queries[BEGIN_T], NULL, NULL, NULL);
        // Add the CPEs obtained from Wazuh-db
        if (node_list && wm_vuldet_insert_cpe_db(db, node_list, 0)) {
            sqlite3_exec(db, vu_queries[END_T], NULL, NULL, NULL);
            mterror(WM_VULNDETECTOR_LOGTAG, VU_WAZUH_DB_CPE_IN_ERROR, atoi(agent->agent_id));
            goto end;
        }

        // Attempts to generate the remaining CPEs
        if (wm_vuldet_generate_agent_cpes(db, agent, 1)) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_AGENT_CPE_GEN_ERROR, atoi(agent->agent_id));
            sqlite3_exec(db, vu_queries[END_T], NULL, NULL, NULL);
            goto end;
        }
        sqlite3_exec(db, vu_queries[END_T], NULL, NULL, NULL);
    }

    retval = 0;
end:
    wm_vuldet_free_cpe_list(node_list);
    os_free(node_list);
    if (obj) {
        cJSON_Delete(obj);
    }

    return retval;
}

void *wm_vuldet_main(wm_vuldet_t * vuldet) {
    wm_vuldet_init(vuldet);

    wm_vuldet_check_db();

    while (1) {
        // Update CVE databases
        if (vuldet->flags.update && wm_vuldet_run_update(vuldet->updates)) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_OVAL_UPDATE_ERROR);
        }

        if ((vuldet->last_detection + (time_t) vuldet->detection_interval) < time(NULL)) {
            wm_vuldet_run_scan(vuldet);
        }

        wm_vuldet_run_sleep(vuldet);
    }

    return NULL;
}

int wm_vuldet_set_agents_info(const char *node_name, agent_software **agents_software, update_node **updates) {
    agent_software *agents = NULL;
    agent_software *f_agent = NULL;
    int set_manager = 1;
    int dist_error = -1;
    bool is_centos = FALSE;
    char str_id[OS_SIZE_128] = "";
    char *name = NULL;
    char *register_ip = NULL;
    char *os_name = NULL;
    char *os_major = NULL;
    char *arch = NULL;
    int build = 0;
    int id = 0;
    int i = 0;
    char *agent_version = NULL;
    char *os_platform = NULL;
    char *agent_major = NULL;
    char *agent_minor = NULL;
    vu_feed agent_dist_ver = -1;
    vu_feed agent_dist = -1;
    int *id_array = NULL;
    cJSON *json_agt_info = NULL;
    cJSON *json_field = NULL;
    int sock = wm_vuldet_get_wdb_socket();

    if (sock < 0) {
        mdebug1("Failed getting Wazuh DB socket connection.");
        return OS_INVALID;
    }

    id_array = wdb_get_agents_by_connection_status(AGENT_CS_ACTIVE, &sock);

    while (set_manager || (id_array && id_array[i] != -1)) {
        dist_error = -1;
        is_centos = FALSE;
        name = NULL;
        register_ip = NULL;
        os_name = NULL;
        os_major = NULL;
        arch = NULL;
        build = 0;

        agent_version = NULL;
        os_platform = NULL;
        agent_major = NULL;
        agent_minor = NULL;

        if (set_manager) {
            id = --set_manager;
        }
        else {
            id = id_array[i++];
        }

        json_agt_info = wdb_get_agent_info(id, &sock);
        if (!json_agt_info) {
            mdebug1("Failed to get agent '%d' information from Wazuh DB.", id);
            continue;
        }

        json_field = cJSON_GetObjectItem(json_agt_info->child, "node_name");
        if(cJSON_IsString(json_field) && json_field->valuestring != NULL) {
            if (0 != strcmp(json_field->valuestring, node_name)) {
                cJSON_Delete(json_agt_info);
                continue;
            }
        }

        json_field = cJSON_GetObjectItem(json_agt_info->child, "os_name");
        if(cJSON_IsString(json_field) && json_field->valuestring != NULL) {
            os_name = json_field->valuestring;
        }

        json_field = cJSON_GetObjectItem(json_agt_info->child, "os_major");
        if(cJSON_IsString(json_field) && json_field->valuestring != NULL) {
            os_major = json_field->valuestring;
        }

        json_field = cJSON_GetObjectItem(json_agt_info->child, "name");
        if(cJSON_IsString(json_field) && json_field->valuestring != NULL) {
            name = json_field->valuestring;
        }

        json_field = cJSON_GetObjectItem(json_agt_info->child, "os_build");
        if(cJSON_IsNumber(json_field)){
            build = json_field->valueint;
        }

        json_field = cJSON_GetObjectItem(json_agt_info->child, "version");
        if (cJSON_IsString(json_field) && json_field->valuestring != NULL) {
            agent_version = json_field->valuestring;
        }

        json_field = cJSON_GetObjectItem(json_agt_info->child, "os_platform");
        if (cJSON_IsString(json_field) && json_field->valuestring != NULL) {
            os_platform = json_field->valuestring;
        }

        if (!os_name) {
            if (name) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AG_NEVER_CON, name);
            }
            continue;
        } else if (!os_major) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UNS_OS_VERSION, id, name?name:"");
            continue;
        } else if (!os_platform) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, "Failed to read platform from agent '%.3d'", id);
            continue;
        }

        if (strcasestr(os_name, vu_feed_ext[FEED_UBUNTU])) {
            if (strstr(os_major, "20")) {
                agent_dist_ver = FEED_FOCAL;
            } else if (strstr(os_major, "18")) {
                agent_dist_ver = FEED_BIONIC;
            } else if (strstr(os_major, "16")) {
                agent_dist_ver = FEED_XENIAL;
            } else if (strstr(os_major, "14")) {
                agent_dist_ver = FEED_TRUSTY;
            } else {
                dist_error = FEED_UBUNTU;
            }
            agent_dist = FEED_UBUNTU;
        } else if (strcasestr(os_name, vu_feed_ext[FEED_DEBIAN])) {
            if (strstr(os_major, "9")) {
                agent_dist_ver = FEED_STRETCH;
            } else if (strstr(os_major, "10")) {
                agent_dist_ver = FEED_BUSTER;
            } else {
                dist_error = FEED_DEBIAN;
            }
            agent_dist = FEED_DEBIAN;
        }  else if (strcasestr(os_name, vu_feed_ext[FEED_REDHAT])) {
            if (strstr(os_major, "8")) {
                agent_dist_ver = FEED_RHEL8;
            } else if (strstr(os_major, "7")) {
                agent_dist_ver = FEED_RHEL7;
            } else if (strstr(os_major, "6")) {
                agent_dist_ver = FEED_RHEL6;
            } else if (strstr(os_major, "5")) {
                agent_dist_ver = FEED_RHEL5;
            } else {
                dist_error = FEED_REDHAT;
            }
            agent_dist = FEED_REDHAT;
        } else if (strcasestr(os_name, vu_feed_ext[FEED_CENTOS])) {
            if (strstr(os_major, "8")) {
                agent_dist_ver = FEED_RHEL8;
            } else if (strstr(os_major, "7")) {
                agent_dist_ver = FEED_RHEL7;
            } else if (strstr(os_major, "6")) {
                agent_dist_ver = FEED_RHEL6;
            } else if (strstr(os_major, "5")) {
                agent_dist_ver = FEED_RHEL5;
            } else {
                dist_error = FEED_CENTOS;
            }
            agent_dist = FEED_REDHAT;
            is_centos = TRUE;
        } else if (strcasestr(os_name, vu_feed_ext[FEED_ARCH])) {
            agent_dist_ver = FEED_ARCH;
            agent_dist= FEED_ARCH;
        } else if (strcasestr(os_name, vu_feed_ext[FEED_WIN])) {
            agent_dist_ver = wm_vuldet_decode_win_os(os_name);
            agent_dist = FEED_WIN;
        } else if (strcasestr(os_platform, vu_feed_ext[FEED_MAC])) {
            char *version = NULL;
            char *save_ptr = NULL;
            w_strdup(agent_version, version);

            if (agent_version) {
                strtok_r(agent_version, "v", &save_ptr);
                agent_major = strtok_r(NULL, ".", &save_ptr);
                agent_minor = strtok_r(NULL, ".", &save_ptr);
                if (!agent_major || !agent_minor) {
                    dist_error = -2;
                    mterror(WM_VULNDETECTOR_LOGTAG, VU_VER_INVALID_FORMAT, id);
                } else {
                    if (atoi(agent_major) > 4 || (atoi(agent_major) == 4 && atoi(agent_minor) > 0)) {
                        agent_dist_ver = FEED_MAC;
                        agent_dist = FEED_MAC;
                    } else {
                        dist_error = -2;
                        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AGENT_UNSOPPORTED, id, version);
                    }
                }
            } else {
                dist_error = -2;
                mterror(WM_VULNDETECTOR_LOGTAG, VU_VER_READING_ERROR, id);
            }

            os_free(version);
        } else {
            // Operating system not supported in any of its versions
            dist_error = -2;
        }

        if (dist_error != -1) {
            // Check if the agent OS can be matched with a OVAL
            if (agent_dist_ver = wm_vuldet_set_allowed_feed(os_name, os_major, updates, &agent_dist), agent_dist_ver == FEED_UNKNOWN) {
                if (dist_error == -2) {
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UNS_OS, id, os_name);
                    continue;
                } else {
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UNS_OS_VERSION, id, vu_feed_ext[dist_error]);
                    continue;
                }
            }
        }

        json_field = cJSON_GetObjectItem(json_agt_info->child, "register_ip");
        if(cJSON_IsString(json_field) && json_field->valuestring != NULL){
            register_ip = json_field->valuestring;
        }

        if (!register_ip) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_NULL_AGENT_IP, id);
            continue;
        }

        json_field = cJSON_GetObjectItem(json_agt_info->child, "os_arch");
        if(cJSON_IsString(json_field) && json_field->valuestring != NULL){
            arch = json_field->valuestring;
        }

        if (!agents) {
            os_calloc(1, sizeof(agent_software), agents);
            f_agent = agents;
        } else {
            os_calloc(1, sizeof(agent_software), agents->next);
            agents = agents->next;
        }

        agents->next = NULL;
        os_strdup(register_ip, agents->agent_ip);

        agents->build = build;

        snprintf(str_id, sizeof(str_id), "%d", id);
        os_strdup(str_id, agents->agent_id);

        if (name) {
            os_strdup(name, agents->agent_name);
        }
        if (arch) {
            os_strdup(arch, agents->arch);
        }
        agents->dist = agent_dist;
        agents->dist_ver = agent_dist_ver;
        agents->flags.centos = is_centos;
        agents->info = 0;
        w_strdup(os_name, agents->os_name);

        cJSON_Delete(json_agt_info);
    }

    *agents_software = f_agent;
    os_free(id_array);
    return OS_SUCCESS;
}

void wm_vuldet_destroy(wm_vuldet_t * vuldet) {
    agent_software *agent;
    update_node **update;
    int i, j;

    for (i = 0, update = vuldet->updates; i < OS_SUPP_SIZE; i++) {
        if (update[i]) {
            os_free(update[i]->dist);
            os_free(update[i]->version);
            os_free(update[i]->url);
            os_free(update[i]->path);
            if (wm_vuldet_is_single_provider(update[i]->dist_ref)) {
                if (update[i]->allowed_os_name) {
                    for (j = 0; update[i]->allowed_os_name[j]; j++) {
                        free(update[i]->allowed_os_name[j]);
                        free(update[i]->allowed_os_ver[j]);
                    }
                    free(update[i]->allowed_os_name);
                    free(update[i]->allowed_os_ver);
                }
            }
            os_free(update[i]);
        }
    }

    for (agent = vuldet->agents_software; agent;) {
        agent_software *agent_aux = agent->next;
        wm_vuldet_free_agent_software(agent);
        if (agent_aux) {
            agent = agent_aux;
        } else {
            break;
        }
    }
    free(vuldet);
}


cJSON *wm_vuldet_dump(const wm_vuldet_t * vuldet){
    cJSON *root = cJSON_CreateObject();
    cJSON *wm_vd = cJSON_CreateObject();
    unsigned int i;

    if (vuldet->flags.enabled) cJSON_AddStringToObject(wm_vd,"enabled","yes"); else cJSON_AddStringToObject(wm_vd,"enabled","no");
    if (vuldet->flags.run_on_start) cJSON_AddStringToObject(wm_vd,"run_on_start","yes"); else cJSON_AddStringToObject(wm_vd,"run_on_start","no");
    cJSON_AddNumberToObject(wm_vd,"interval",vuldet->detection_interval);
    cJSON_AddNumberToObject(wm_vd,"ignore_time",vuldet->ignore_time);
    cJSON *providers = cJSON_CreateArray();

    for (i = 0; i < OS_SUPP_SIZE; i++) {
        if (vuldet->updates[i] && i != CPE_WDIC && i != CVE_MSU) {
            cJSON *provider = cJSON_CreateObject();
            if (vuldet->updates[i]->dist) cJSON_AddStringToObject(provider,"name",vuldet->updates[i]->dist);
            if (vuldet->updates[i]->version) cJSON_AddStringToObject(provider,"version",vuldet->updates[i]->version);
            if (vuldet->updates[i]->dist && (!strcmp(vuldet->updates[i]->dist, "redhat") || !strcmp(vuldet->updates[i]->dist, "nvd"))) {
                if (vuldet->updates[i]->update_from_year) cJSON_AddNumberToObject(provider,"update_from_year",vuldet->updates[i]->update_from_year);
            }
            if (vuldet->updates[i]->url) {     // URL for Canonical and Debian providers
                cJSON_AddStringToObject(provider,"url",vuldet->updates[i]->url);
            } else if (vuldet->updates[i]->multi_url) {     // URL for RedHat and NVD providers
                cJSON_AddStringToObject(provider,"url",vuldet->updates[i]->multi_url);
                cJSON *url_attrs = cJSON_CreateObject();
                int add = 0;
                if (vuldet->updates[i]->multi_url_start > 0) {
                    cJSON_AddNumberToObject(url_attrs,"start",vuldet->updates[i]->multi_url_start);
                    add = 1;
                }
                if (vuldet->updates[i]->multi_url_end > 0) {
                    cJSON_AddNumberToObject(url_attrs,"end",vuldet->updates[i]->multi_url_end);
                    add = 1;
                }
                if (vuldet->updates[i]->port > 0) {
                    cJSON_AddNumberToObject(url_attrs,"port",vuldet->updates[i]->port);
                    add = 1;
                }
                if (add) {
                    cJSON_AddItemToObject(provider,"url_attrs",url_attrs);
                } else {
                    cJSON_Delete(url_attrs);
                }
            }
            if (vuldet->updates[i]->path) {
                cJSON_AddStringToObject(provider,"path",vuldet->updates[i]->path);
            } else if (vuldet->updates[i]->multi_path){
                cJSON_AddStringToObject(provider,"path",vuldet->updates[i]->multi_path);
            }

            cJSON_AddNumberToObject(provider,"update_interval",vuldet->updates[i]->interval);
            cJSON_AddNumberToObject(provider,"download_timeout",vuldet->updates[i]->timeout);

            // Allowed OS
            if (wm_vuldet_is_single_provider(vuldet->updates[i]->dist_ref)) {
                if (vuldet->updates[i]->allowed_os_name && vuldet->updates[i]->allowed_os_name[0]) {
                    int j = 0;
                    cJSON *allow = cJSON_CreateArray();
                    cJSON *allowed = cJSON_CreateObject();
                    cJSON *src = cJSON_CreateArray();
                    for (j = 0; vuldet->updates[i]->allowed_os_name[j]; j++) {
                        char * src_aux;
                        os_calloc(OS_SIZE_128, sizeof(char), src_aux);
                        // It is mandatory to have name + version, the only valid format is name-version
                        snprintf(src_aux, OS_SIZE_128 - 1, "%s-%s", vuldet->updates[i]->allowed_os_name[j], vuldet->updates[i]->allowed_os_ver[j]);
                        cJSON_AddItemToArray(src, cJSON_CreateString(src_aux));
                        os_free(src_aux);
                    }
                    cJSON_AddItemToObject(allowed, "src", src);
                    cJSON_AddItemToArray(allow, allowed);
                    cJSON_AddItemToObject(provider,"allow",allow);
                }
            }

            cJSON_AddItemToArray(providers, provider);
        }
    }

    if (cJSON_GetArraySize(providers) > 0) {
        cJSON_AddItemToObject(wm_vd,"providers",providers);
    } else {
        cJSON_free(providers);
    }

    cJSON_AddItemToObject(root,"vulnerability-detector",wm_vd);

    return root;

}

char *vu_get_version() {
    static char version[10] = { '\0' };
    int major = 0;
    int minor = 0;
    int patch = 0;

    if (*version == '\0') {
        sscanf(__ossec_version, "v%d.%d.%d", &major, &minor, &patch);
        snprintf(version, 9, "%d%d%d", major, minor, patch);
    }

    return version;
}

int wm_vuldet_db_empty(sqlite3 *db, vu_feed version) {

    sqlite3_stmt *stmt = NULL;
    int result = 0;

    if (wm_vuldet_prepare(db, vu_queries[VU_CHECK_DB_CONTENT], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, vu_feed_tag[version], -1, NULL);

    if (result = wm_vuldet_step(stmt), result == SQLITE_DONE ||
        (result == SQLITE_ROW && !sqlite3_column_int(stmt, 0))) {
        result = 0;
    } else {
        result = 1;
    }

    wdb_finalize(stmt);

    return result;
}

int wm_vuldet_nvd_empty() {
    sqlite3 *db;
    sqlite3_stmt *stmt = NULL;
    int result;

    if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, NULL);
    }

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_NVD_COUNT], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    if (result = wm_vuldet_step(stmt), result == SQLITE_DONE ||
        (result == SQLITE_ROW && !sqlite3_column_int(stmt, 0))) {
        // NVD DB empty
        result = 0;
    } else {
        result = 1;
    }

    wdb_finalize(stmt);
    sqlite3_close_v2(db);

    return result;
}

const char *wm_vuldet_get_unified_severity(char *severity) {

    if (!severity || strcasestr(severity, vu_severities[VU_UNKNOWN]) || strcasestr(severity, vu_severities[VU_UNTR])) {
        return vu_severities[VU_UNDEFINED_SEV];
    } else if (strcasestr(severity, vu_severities[VU_LOW]) || strcasestr(severity, vu_severities[VU_NEGL])) {
        return vu_severities[VU_LOW];
    } else if (strcasestr(severity, vu_severities[VU_MEDIUM]) || strcasestr(severity, vu_severities[VU_MODERATE])) {
        return vu_severities[VU_MEDIUM];
    } else if (strcasestr(severity, vu_severities[VU_HIGH]) || strcasestr(severity, vu_severities[VU_IMPORTANT])) {
        return vu_severities[VU_HIGH];
    } else if (strcasestr(severity, vu_severities[VU_CRITICAL])) {
        return vu_severities[VU_CRITICAL];
    } else if (strcasestr(severity, vu_severities[VU_NONE])) {
        return vu_severities[VU_NONE];
    }
    return vu_severities[VU_UNDEFINED_SEV];

}

void wm_vuldet_get_package_os(const char *version, const char **os_major, char **os_minor) {
    char *v_it;

    if (v_it = strstr(version, vu_package_dist[VU_RH_EXT_8]), v_it) {
        *os_major = vu_feed_tag[FEED_RHEL8];
    } else if (v_it = strstr(version, vu_package_dist[VU_RH_EXT_7]), v_it) {
        *os_major = vu_feed_tag[FEED_RHEL7];
    } else if (v_it = strstr(version, vu_package_dist[VU_RH_EXT_6]), v_it) {
        *os_major = vu_feed_tag[FEED_RHEL6];
    } else if (v_it = strstr(version, vu_package_dist[VU_RH_EXT_5]), v_it) {
        *os_major = vu_feed_tag[FEED_RHEL5];
    }

    wm_vuldet_set_subversion(v_it, os_minor);
}

void wm_vuldet_set_subversion(char *version, char **os_minor) {
    if (version && (version = strchr(version, '_')) && *(++version) != '\0') {
        size_t minor_size = strlen(version) + 1;
        os_calloc(minor_size + 1, sizeof(char), *os_minor);
        snprintf(*os_minor, minor_size, "%ld", strtol(version, NULL, 10));
    }
}

int wm_vuldet_generate_os_cpe(sqlite3 *db, agent_software *agent, int *min_cpe_index, cpe_list **node_list) {
    if (agent->dist_ver >= FEED_WS2003 && agent->dist_ver <= FEED_WS2022) {
        // It is a supported Windows agent
        if (!wm_vuldet_generate_win_cpe(agent)) {
            int i;
            for (i = 0; agent->agent_os_cpe[i]; i++) {
                if (wm_vuldet_insert_agent_data(db, agent, min_cpe_index, "",
                                            vu_feed_ext[agent->dist_ver], "", "", NULL, "",
                                            agent->agent_os_cpe[i], node_list)) {
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_INSERT_DATA_ERR, agent->agent_os_cpe[i]->raw);
                    return OS_INVALID;
                }
            }
        }
    }

    return 0;
}

int wm_vuldet_generate_win_cpe(agent_software *agent) {
    STATIC const char *PR_WINXP = "windows_xp";
    STATIC const char *PR_WINVISTA = "windows_vista";
    STATIC const char *PR_WIN7 = "windows_7";
    STATIC const char *PR_WIN8 = "windows_8";
    STATIC const char *PR_WIN81 = "windows_8.1";
    STATIC const char *PR_WIN10 = "windows_10";
    STATIC const char *PR_WIN2003 = "windows_server_2003";
    STATIC const char *PR_WIN2008 = "windows_server_2008";
    STATIC const char *PR_WIN2012 = "windows_server_2012";
    STATIC const char *PR_WIN2016 = "windows_server_2016";
    STATIC const char *PR_WIN2019 = "windows_server_2019";
    STATIC const char *PR_WIN2022 = "windows_server_2022";
    STATIC const char *VER_R2 = "r2";
    const char *win_pr = NULL;
    const char *win_ver = NULL;
    const char *win_upd = NULL;
    char *ver_aux = NULL;

    if (!agent->os_release && agent->dist_ver != FEED_WS2012 && agent->dist_ver != FEED_WS2012R2) {
        mtwarn(WM_VULNDETECTOR_LOGTAG, VU_OSINFO_DISABLED, atoi(agent->agent_id));
    }

    if (agent->os_display_version && *agent->os_display_version != ' ') {
        ver_aux = w_tolower_str(agent->os_display_version);
    } else {
        ver_aux = agent->os_release;
    }

    switch (agent->dist_ver) {
        case FEED_WS2003:
            win_pr = PR_WIN2003;
            win_upd = ver_aux;
        break;
        case FEED_WS2003R2:
            win_pr = PR_WIN2003;
            win_ver = VER_R2;
            win_upd = ver_aux;
        break;
        case FEED_WXP:
            win_pr = PR_WINXP;
            win_upd = ver_aux;
        break;
        case FEED_WVISTA:
            win_pr = PR_WINVISTA;
            win_upd = ver_aux;
        break;
        case FEED_W7:
            win_pr = PR_WIN7;
            win_upd = ver_aux;
        break;
        case FEED_W8:
            win_pr = PR_WIN8;
            win_upd = ver_aux;
        break;
        case FEED_W81:
            win_pr = PR_WIN81;
            win_upd = ver_aux;
        break;
        case FEED_W10:
            win_pr = PR_WIN10;
            win_ver = ver_aux;
        break;
        case FEED_WS2008:
            win_pr = PR_WIN2008;
            win_upd = ver_aux;
        break;
        case FEED_WS2008R2:
            win_pr = PR_WIN2008;
            win_ver = VER_R2;
            win_upd = ver_aux;
        break;
        case FEED_WS2012:
            win_pr = PR_WIN2012;
        break;
        case FEED_WS2012R2:
            win_pr = PR_WIN2012;
            win_ver = VER_R2;
        break;
        case FEED_WS2016:
            win_pr = PR_WIN2016;
            win_ver = ver_aux;
        break;
        case FEED_WS2019:
            win_pr = PR_WIN2019;
            win_ver = ver_aux;
        break;
        case FEED_WS2022:
            win_pr = PR_WIN2022;
            win_ver = ver_aux;
        break;
        default:
            return 1;
    }

    // We will insert a double CPE for the following systems
    int os_cpes = wm_vuldet_double_os_cpe(agent->dist_ver) ? 2 : 1;
    os_calloc(os_cpes + 1, sizeof(cpe *), agent->agent_os_cpe);

    int i;
    for (i = 0; i < os_cpes; i++) {
        agent->agent_os_cpe[i] = wm_vuldet_generate_cpe("o",
                                                    "microsoft",
                                                    win_pr,
                                                    !i && win_ver ? win_ver : "-",
                                                    !i ? win_upd : win_ver,
                                                    NULL,
                                                    NULL,
                                                    NULL,
                                                    NULL,
                                                    agent->arch,
                                                    NULL,
                                                    0,
                                                    vu_feed_ext[agent->dist_ver]);
    }
    return 0;
}

int wm_vuldet_discard_kernel_package(agent_software *agent, const char *name,
                                    const char *version, const char *arch) {
    char *kernel_uname = NULL;

    if (!agent->kernel_release) {
        return 0;
    }

    if (agent->dist == FEED_REDHAT) {
        // Only kernel images have as package name "kernel".
        // "kernel-rt" is a realtime kernel only available for RedHat.
        if (strcmp(name, "kernel") && strcmp(name, "kernel-rt")) {
            return 0;
        }
        size_t len = snprintf(kernel_uname, 0, "%s.%s", version, arch);
        os_calloc(1, len + 1, kernel_uname);
        snprintf(kernel_uname, len + 1, "%s.%s", version, arch);

    } else { // For Debian family
        os_calloc(1, strlen(name) + 1, kernel_uname);
        if (!sscanf(name, "linux-image-%s", kernel_uname) || strstr(name, "linux-image-generic")) {
            os_free(kernel_uname);
            return 0;
        }
    }

    if (strcmp(kernel_uname, agent->kernel_release)) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_DISCARD_KERNEL_PKG, kernel_uname, atoi(agent->agent_id));
        os_free(kernel_uname);
        return -1;
    }

    os_free(kernel_uname);

    return 0;
}

int wm_vuldet_generate_os_and_kernel_package(sqlite3 *db, agent_software *agent) {
    const char *product_name[MAX_PRODUCT_NAMES] = {0};
    const char *vendor = NULL;

    if (!db) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_DB_NOT_INITIALIZED);
        return OS_INVALID;
    }

    if (!agent || !agent->agent_id) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_AGENT_NOT_INITIALIZED);
        return OS_INVALID;
    }

    if (!agent->os_release && !agent->kernel_release) {
        mtwarn(WM_VULNDETECTOR_LOGTAG, VU_OSINFOLNX_DISABLED, atoi(agent->agent_id));
    }

    switch (agent->dist_ver) {
        case FEED_TRUSTY:
        case FEED_XENIAL:
        case FEED_BIONIC:
        case FEED_FOCAL:
            product_name[0] = PR_UBUNTU;
            vendor = V_UBUNTU;
        break;
        case FEED_STRETCH:
        case FEED_BUSTER:
            product_name[0] = PR_DEBIAN;
            vendor = V_DEBIAN;
        break;
        case FEED_RHEL5:
        case FEED_RHEL6:
        case FEED_RHEL7:
        case FEED_RHEL8:
            product_name[0] = PR_REDHAT;
            vendor = V_REDHAT;
        break;
        case FEED_ARCH:
            product_name[0] = PR_ARCH;
            vendor = V_ARCH;
        break;
        case FEED_MAC:
            vendor = "apple";
            if (!strcmp(agent->os_name, "Mac OS X Server")) {
                product_name[0] = "mac_os_x_server";
            } else {
                product_name[0] = "mac_os_x";
                product_name[1] = "mac_os";
                product_name[2] = "macos";
            }
        break;
        default:
            return 1;
    }

    for (int i = 0; product_name[i] != NULL; i++) {
        // Generate os package
        if (agent->os_release) {
            if (wm_vuldet_insert_agent_data(db, agent, NULL, vendor, product_name[i], NULL, agent->os_release, NULL, agent->arch, NULL, NULL)) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_INSERT_DATA_ERR, product_name[i]);
                return OS_INVALID;
            }
        }
    }

    // Generate kernel package
    if (agent->dist != FEED_MAC && agent->kernel_release) {
        char *release, *release_end;
        os_strdup(agent->kernel_release, release);
        if (release_end = strstr(release, "-"), release_end) {
            *release_end = '\0';
        }

        if (wm_vuldet_insert_agent_data(db, agent, NULL, V_KERNEL, PR_KERNEL, NULL, release, NULL, agent->arch, NULL, NULL)) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_INSERT_DATA_ERR, PR_KERNEL);
            os_free(release);
            return OS_INVALID;
        }

        os_free(release);
    }

    return 0;
}

vu_feed wm_vuldet_decode_win_os(char *os_raw) {
    if (strcasestr(os_raw, vu_feed_ext[FEED_WS2003R2])) {
        return FEED_WS2003R2;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2003])) {
        return FEED_WS2003;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WXP])) {
        return FEED_WXP;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WVISTA])) {
        return FEED_WVISTA;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_W7])) {
        return FEED_W7;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_W81])) {
        return FEED_W81;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_W8])) {
        return FEED_W8;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_W10])) {
        return FEED_W10;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2008R2])) {
        return FEED_WS2008R2;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2008])) {
        return FEED_WS2008;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2012R2])) {
        return FEED_WS2012R2;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2012])) {
        return FEED_WS2012;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2016])) {
        return FEED_WS2016;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2019])) {
        return FEED_WS2019;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2022])) {
        return FEED_WS2022;
    } else {
        STATIC OSHash *unk_os = NULL;
        STATIC int os_count = 0;

        if (!os_count) {
            if (unk_os = OSHash_Create(), !unk_os) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_CREATE_HASH_ERRO, "unknown_systems");
                pthread_exit(NULL);
            }
        }

        if (os_count < 20 && OSHash_Add(unk_os, os_raw, NULL) == 2) {
            mtwarn(WM_VULNDETECTOR_LOGTAG, VU_UNC_SYSTEM, os_raw);
            os_count++;
        }
        return FEED_WIN;
    }
}

int wm_vuldet_insert_agent_data(sqlite3 *db, agent_software *agent, int *cpe_index, const char *vendor, const char *product, const char *source, const char *version, const char *src_version, const char *arch, cpe *ag_cpe, cpe_list **node_list) {
    sqlite3_stmt *stmt = NULL;
    int retval = OS_INVALID;
    int result;
    const char *os_major = NULL;
    char *os_minor = NULL;
    char *normalized_name = NULL;
    char *normalized_source = NULL;
    char *normalized_vendor = NULL;

    if (agent->dist == FEED_REDHAT) {
        wm_vuldet_get_package_os(version, &os_major, &os_minor);
    }

    if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_AGENTS], -1, &stmt, NULL) != SQLITE_OK) {
        wm_vuldet_sql_error(db, stmt);
        goto end;
    }

    sqlite3_bind_text(stmt, 1, agent->agent_id, -1, NULL);
    sqlite3_bind_text(stmt, 2, os_major, -1, NULL);
    sqlite3_bind_text(stmt, 3, os_minor, -1, NULL);
    sqlite3_bind_int(stmt, 4, ag_cpe ? *cpe_index : 0);

    if (agent->dist == FEED_MAC) {
        normalized_vendor = w_tolower_str(vendor);
        sqlite3_bind_text(stmt, 5, normalized_vendor, -1, NULL);
    } else {
        sqlite3_bind_text(stmt, 5, vendor, -1, NULL);
    }

    if (agent->dist != FEED_WIN) {
        /* In Windows the package name is
        normalized by the CPE helper */
        normalized_name = w_tolower_str(product);
        sqlite3_bind_text(stmt, 6, normalized_name, -1, NULL);
        normalized_source = w_tolower_str(source);
        sqlite3_bind_text(stmt, 7, normalized_source, -1, NULL);
    } else {
        sqlite3_bind_text(stmt, 6, product, -1, NULL);
        sqlite3_bind_text(stmt, 7, source, -1, NULL);
    }

    sqlite3_bind_text(stmt, 8, version, -1, NULL);
    sqlite3_bind_text(stmt, 9, src_version, -1, NULL);
    sqlite3_bind_text(stmt, 10, arch, -1, NULL);

    if (ag_cpe) {
        if (!*node_list) {
            os_calloc(1, sizeof(cpe_list), *node_list);
        }

        if (wm_vuldet_add_cpe(ag_cpe, NULL, *node_list, *cpe_index)) {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_AGENT_CPE_PARSE_ERROR, ag_cpe->raw, atoi(agent->agent_id));
        } else {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_AGENT_CPE_RECV, ag_cpe->raw, atoi(agent->agent_id));
            *cpe_index = *cpe_index - 1;
        }
    }

    if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
        wm_vuldet_sql_error(db, stmt);
        goto end;
    }

    retval = 0;

end:
    os_free(os_minor);
    os_free(normalized_name);
    os_free(normalized_vendor);
    os_free(normalized_source);
    wdb_finalize(stmt);
    return retval;
}

void wm_vuldet_free_agent_software(agent_software *agent) {
    if (agent) {
        free(agent->agent_id);
        free(agent->agent_name);
        free(agent->arch);
        free(agent->agent_ip);
        free(agent->os_release);
        os_free(agent->os_display_version);
        free(agent->kernel_release);
        int i;
        for (i = 0; agent->agent_os_cpe && agent->agent_os_cpe[i]; i++) {
            wm_vuldet_free_cpe(agent->agent_os_cpe[i]);
        }
        free(agent->agent_os_cpe);
        free(agent->os_name);
        free(agent);
    }
}

int wm_vuldet_fetch_arch(update_node *update, char * repo) {
    int res_url_request;

    if (*repo == '\0') {
        snprintf(repo, OS_SIZE_2048, "%s", JSON_ARCH_REPO);
    }

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DOWNLOAD_START, repo);

    for (int8_t attempts = 0;; attempts++) {
        res_url_request = wurl_request_uncompress_bz2_gz(repo, VU_FIT_TEMP_FILE, NULL, NULL, update->timeout, NULL);

        if (!res_url_request) {
            break;
        } else if (attempts == ARCH_REPO_MAX_ATTEMPTS) {
            mtwarn(WM_VULNDETECTOR_LOGTAG, VU_API_REQ_INV, repo, attempts);
            return VU_INV_FEED;
        }

        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DOWNLOAD_FAIL, attempts * DOWNLOAD_SLEEP_FACTOR);
        sleep(attempts * DOWNLOAD_SLEEP_FACTOR);
    }

    return 0;
}

int wm_vuldet_fetch_MSU(update_node *update, char *repo) {
    char *hash = NULL;

    if (*repo == '\0') {
        repo = MSU_REPO;
        if (wm_vuldet_update_MSU(update) == false) {
            return VU_NOT_NEED_UPDATE;
        }
        hash = wm_vuldet_get_hash(vu_feed_tag[update->dist_tag_ref]);
        if (hash == NULL) {
            merror("Failed to retrieve hash value. File integrity won't be checked.");
        }
    }

    int res_url_request;

    for (int8_t attempts = 0;; attempts++) {
        res_url_request = wurl_request_uncompress_bz2_gz(repo, VU_FIT_TEMP_FILE, NULL, NULL, update->timeout, hash);

        if (!res_url_request) {
            break;
        } else if (attempts == WM_VULNDETECTOR_DOWN_ATTEMPTS) {
            os_free(hash);
            return VU_INV_FEED;
        }

        mdebug1(VU_DOWNLOAD_FAIL, attempts);
        sleep(attempts);
    }

    os_free(hash);

    return 0;
}

bool wm_vuldet_update_MSU(update_node *update) {
    feed_metadata *msu = NULL;
    if (msu = wm_vuldet_fetch_MSU_metadata(update), msu == NULL){
        return true;
    }

    bool retval = true;

    if (!msu->sha256 || !msu->last_mod) {
        goto clean;
    }

    if (wm_vuldet_check_timestamp(vu_feed_tag[update->dist_tag_ref],
        msu->last_mod, NULL) == VU_TIMESTAMP_UPDATED) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_DATE, update->dist_ext);
        retval = false;
    } else {
        wm_insert_MSU_metadata(msu, update);
    }

clean:
    os_free(msu->version);
    os_free(msu->sha256);
    os_free(msu->last_mod);
    os_free(msu->size);
    os_free(msu->g_size);
    os_free(msu);

    return retval;
}

feed_metadata * wm_vuldet_fetch_MSU_metadata(update_node *update) {
    int res_url_request;

    for (int8_t attempts = 0;; attempts++) {
        res_url_request = wurl_request(MSU_REPO_META, VU_TEMP_FILE, NULL, NULL, update->timeout);

        if (!res_url_request) {
            break;
        } else if (attempts == WM_VULNDETECTOR_DOWN_ATTEMPTS) {
            return NULL;
        }

        mdebug1(VU_DOWNLOAD_FAIL, attempts);
        sleep(attempts);
    }

    return wm_vuldet_parse_MSU_metadata(VU_TEMP_FILE);
}

feed_metadata * wm_vuldet_parse_MSU_metadata(char *path) {
    FILE *fp = NULL;
    feed_metadata *msu = NULL;

    if (fp = fopen(path, "r"), fp == NULL) {
        return msu;
    }

    char buffer[OS_MAXSTR + 1] = {0};

    os_calloc(1, sizeof(feed_metadata), msu);

    while (fgets(buffer, OS_MAXSTR, fp)) {
        // Remove new line character
        char * end;
        if ((end = strchr(buffer, '\n')) != NULL) {
            *end = '\0';
        }

        char * delim;
        if (delim = strchr(buffer, ':'), !delim) {
            continue;
        }
        if (!strncmp(buffer, "lastModifiedDate", 16)) {
            os_strdup(delim + 1, msu->last_mod);
        } else if (!strncmp(buffer, "sha256", 6)) {
            os_strdup(delim + 1, msu->sha256);
        } else if (!strncmp(buffer, "size", 4)) {
            os_strdup(delim + 1, msu->size);
        } else if (!strncmp(buffer, "gzSize", 6)) {
            os_strdup(delim + 1, msu->g_size);
        } else if (!strncmp(buffer, "version", 7)) {
            os_strdup(delim + 1, msu->version);
        }
    }

    fclose(fp);

    return msu;
}

int wm_insert_MSU_metadata(feed_metadata *msu, update_node *update) {
    sqlite3_stmt *stmt = NULL;
    sqlite3 *db = NULL;

    if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_METADATA], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, vu_feed_tag[update->dist_tag_ref], -1, NULL);
    sqlite3_bind_text(stmt, 2, vu_feed_ext[update->dist_tag_ref], -1, NULL);
    sqlite3_bind_text(stmt, 3, (msu->version)? msu->version : "1", -1, NULL);
    sqlite3_bind_text(stmt, 4, (msu->version)? msu->version : "1", -1, NULL);
    sqlite3_bind_text(stmt, 5, msu->last_mod, -1, NULL);
    sqlite3_bind_text(stmt, 6, msu->sha256, -1, NULL);
    sqlite3_bind_int(stmt, 7, (msu->size)? atoi(msu->size) : 0);
    sqlite3_bind_int(stmt, 8, (msu->g_size)? atoi(msu->g_size) : 0);

    if (wm_vuldet_step(stmt) != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }


    sqlite3_close_v2(db);
    wdb_finalize(stmt);

    return 0;
}

int wm_vuldet_fetch_wazuh_cpe(update_node *update) {
    int retval = VU_INV_FEED;
    char buffer[OS_MAXSTR + 1];
    FILE *fp = NULL;
    FILE *fp_out;
    STATIC const char *UPDATE_DATE_TAG = "update_date";

    if (fp = fopen(VU_CPE_HELPER_FILE, "r"), !fp) {
        goto end;
    }
    if (fp_out = fopen(VU_FIT_TEMP_FILE, "w"), !fp_out) {
        goto end;
    }

    while (fgets(buffer, OS_MAXSTR, fp)) {
        if (*buffer == '=') {
            continue;
        }
        fwrite(buffer, 1, strlen(buffer), fp_out);
    }
    fclose(fp);
    fclose(fp_out);

    if (fp = fopen(VU_FIT_TEMP_FILE, "r"), !fp) {
        goto end;
    }

    // Check the timestamp
    while (fgets(buffer, OS_MAXSTR, fp)) {
        char *timestamp_base;
        char *timestamp_end;

        if ((timestamp_base = strstr(buffer, UPDATE_DATE_TAG)) &&
            (timestamp_base = strchr(timestamp_base, ':')) &&
            (timestamp_base = strchr(timestamp_base, '\"')) &&
            (timestamp_end = strchr(++timestamp_base, '\"'))) {
            char timestamp[OS_SIZE_256 + 1];

            *timestamp_end = '\0';
            switch (wm_vuldet_check_timestamp(vu_feed_tag[update->dist_tag_ref], timestamp_base, timestamp)) {
                case VU_TIMESTAMP_FAIL:
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED_ERROR, update->dist_ext);
                    goto end;
                break;
                case VU_TIMESTAMP_UPDATED:
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_DATE, update->dist_ext);
                    retval = VU_NOT_NEED_UPDATE;
                    goto end;
                break;
                case VU_TIMESTAMP_OUTDATED:
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED, update->dist_ext, "");
                break;
            }
            break;
        }
    }

    retval = 0;
end:
    if (fp) {
        fclose(fp);
    }

    return retval;
}

int wm_vuldet_json_wcpe_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities) {
    cJSON *json_it;
    vu_cpe_dic *wcpes_it = NULL;
    STATIC char *JSON_WCPE_FORMAT_VERSION = "format_version";
    STATIC char *JSON_WCPE_UPDATE_DATE = "update_date";
    STATIC char *JSON_WCPE_DICTIONARY = "dictionary";
    char *timestamp;
    char *format_version_major;
    int major_dec;

    // Check the timestamp
    if (json_it = cJSON_GetObjectItem(json_feed, JSON_WCPE_UPDATE_DATE), !json_it) {
        return OS_INVALID;
    }

    if (timestamp = json_it->valuestring, !timestamp) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_WCPE_GET_TIMEST_ERROR);
        return OS_INVALID;
    }

    switch (wm_vuldet_check_timestamp(vu_feed_tag[FEED_CPEW], timestamp, NULL)) {
        case VU_TIMESTAMP_FAIL:
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED_ERROR, vu_feed_ext[FEED_CPEW]);
            return OS_INVALID;
        break;
        case VU_TIMESTAMP_UPDATED:
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_DATE, vu_feed_ext[FEED_CPEW]);
            return VU_NOT_NEED_UPDATE;
        break;
        case VU_TIMESTAMP_OUTDATED:
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED, vu_feed_ext[FEED_CPEW], "");
        break;
    }

    // Check the version format
    if (json_it = cJSON_GetObjectItem(json_feed, JSON_WCPE_FORMAT_VERSION), !json_it) {
        return OS_INVALID;
    }

    if (format_version_major = json_it->valuestring, !format_version_major) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_WCPE_GET_VERFORMAT_ERROR);
        return OS_INVALID;
    }

    if (major_dec = strtol(format_version_major, NULL, 10), major_dec < 1 || major_dec > VU_CPEHELPER_FORMAT_VERSION) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_WCPE_INV_VERFORMAT, format_version_major);
        return OS_INVALID;
    }

    for (json_it = json_feed->child; json_tagged_obj(json_it); json_it = json_it->next) {
        if (!wcpes_it) {
            os_calloc(1, sizeof(vu_cpe_dic), wcpes_it);
        }
        if (!strcmp(json_it->string, vu_cpe_tags[CPE_VERSION])) {
            if (w_strdup(json_it->valuestring, wcpes_it->version)) {
                goto error;
            }
        } else if (!strcmp(json_it->string, JSON_WCPE_FORMAT_VERSION)) {
            if (w_strdup(json_it->valuestring, wcpes_it->format_version)) {
                goto error;
            }
        } else if (!strcmp(json_it->string, JSON_WCPE_UPDATE_DATE)) {
            if (w_strdup(json_it->valuestring, wcpes_it->update_date)) {
                goto error;
            }
        } else if (!strcmp(json_it->string, JSON_WCPE_DICTIONARY)) {
            wm_vuldet_json_dic_parser(json_it->child, wcpes_it);
        }
    }
    parsed_vulnerabilities->w_cpes = wcpes_it;

    return VU_NEED_UPDATE;
error:
    os_free(wcpes_it->version);
    os_free(wcpes_it->format_version);
    os_free(wcpes_it->update_date);
    os_free(wcpes_it);

    return OS_INVALID;
}

int wm_vuldet_json_dic_parser(cJSON *json_feed, vu_cpe_dic *wcpes) {
    cJSON *dic_node;
    char need_check_hotfix = 0;
    int i;
    STATIC char *JSON_WCPE_ACTION = "action";
    STATIC char *JSON_WCPE_TARGET = "target";
    STATIC char *JSON_WCPE_SOURCE = "source";
    STATIC char *JSON_WCPE_TRANSLATION = "translation";

    for (; json_feed; json_feed = json_feed->next) {
        char assigned = 0;
        vu_cpe_dic_node *node;
        os_calloc(1, sizeof(vu_cpe_dic_node), node);
        for (dic_node = json_feed->child; json_tagged_obj(dic_node); dic_node = dic_node->next) {
            if (!strcmp(dic_node->string, JSON_WCPE_TARGET) && dic_node->valuestring) {
                vu_cpe_dic_node *node_ref = NULL;

                for (i = 0; wcpes->targets_name && wcpes->targets_name[i]; i++) {
                    if (!strcmp(wcpes->targets_name[i], dic_node->valuestring)) {
                        node_ref = wcpes->targets[i];
                        break;
                    }
                }

                if (!node_ref) {
                    os_realloc(wcpes->targets_name, (i + 2) * sizeof(char *), wcpes->targets_name);
                    os_realloc(wcpes->targets, (i + 2) * sizeof(vu_cpe_dic_node *), wcpes->targets);
                    os_strdup(dic_node->valuestring, wcpes->targets_name[i]);
                    wcpes->targets[i + 1] = NULL;
                    wcpes->targets_name[i + 1] = NULL;
                } else {
                    node->prev = wcpes->targets[i];
                    wcpes->targets[i]->next = node;
                }

                wcpes->targets[i] = node;
                assigned = 1;
            } else if (!strcmp(dic_node->string, JSON_WCPE_ACTION)) {
                int msu_mask = CPE_DIC_REP_MSUN_IF_VER_MATCHES | CPE_DIC_REP_MSUN;

                node->action = wm_vuldet_fill_action_mask(dic_node->child);
                if ((node->action & msu_mask && !(node->action & CPE_DIC_CHECK_HOTFIX)) ||
                    (node->action & CPE_DIC_CHECK_HOTFIX && !(node->action & msu_mask))) {
                    need_check_hotfix = 1;
                }
            } else if (!strcmp(dic_node->string, JSON_WCPE_SOURCE)) {
                wm_vuldet_fill_dic_section(dic_node->child, &node->source);
            } else if (!strcmp(dic_node->string, JSON_WCPE_TRANSLATION)) {
                wm_vuldet_fill_dic_section(dic_node->child, &node->translation);
            }
        }

        if (!assigned) {
            os_free(node);
        }
    }

    if (need_check_hotfix) {
        mtwarn(WM_VULNDETECTOR_LOGTAG, VU_CPEHELPER_INVALID_ACTION, vu_cpe_dic_option[VU_REP_MSUN_IF_VER_MATCHES], vu_cpe_dic_option[VU_CHECK_HOTFIX]);
    }

    return 0;
}

int wm_vuldet_fill_dic_section(cJSON *json_feed, vu_cpe_dic_section **wcpes) {
    cJSON *json_array;

    if (!*wcpes) {
        os_calloc(1, sizeof(vu_cpe_dic_section), *wcpes);
    }

    for (; json_tagged_obj(json_feed); json_feed = json_feed->next) {
        char ****array;
        int i;

        if (!strcmp(json_feed->string, vu_cpe_tags[CPE_VENDOR])) {
            array = &(*wcpes)->vendor;
            if (!json_feed->child) {
                os_realloc(*array, 2 * sizeof(char **), *array);
                (*array)[1] = NULL;
                os_calloc(2, sizeof(char *), (*array)[0]);
                os_strdup("", ***array);
            }
        } else if (!strcmp(json_feed->string, vu_cpe_tags[CPE_PRODUCT])) {
            array = &(*wcpes)->product;
        } else if (!strcmp(json_feed->string, vu_cpe_tags[CPE_VERSION])) {
            array = &(*wcpes)->version;
        } else if (!strcmp(json_feed->string, vu_cpe_tags[CPE_SW_EDITION])) {
            array = &(*wcpes)->sw_edition;
        } else if (!strcmp(json_feed->string, vu_cpe_tags[CPE_TARGET_HW])) {
            array = &(*wcpes)->target_hw;
        } else if (!strcmp(json_feed->string, vu_cpe_tags[CPE_MSU_NAME])) {
            array = &(*wcpes)->msu_name;
        } else {
            mtwarn(WM_VULNDETECTOR_LOGTAG, VU_INVALID_DIC_TAG, json_feed->string);
            continue;
        }

        for (i = 0, json_array = json_feed->child; json_array; json_array = json_array->next, i++) {
            os_realloc(*array, (i + 2) * sizeof(char **), *array);
            memset(&(*array)[i], 0, 2 * sizeof(char **));

            if (json_array->type == cJSON_String) {
                os_calloc(2, sizeof(char *), (*array)[i]);
                w_strdup(json_array->valuestring, (*array)[i][0]);
            } else if (json_array->type == cJSON_Array) {
                cJSON *json_sub_array = json_array->child;
                int j = 0;

                for (; json_sub_array; json_sub_array = json_sub_array->next, j++) {
                    if (json_sub_array->type != cJSON_String) {
                        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_ENTRY_TYPE, -1 * json_array->type);
                        j--;
                    } else {
                        os_realloc((*array)[i], (j + 2) * sizeof(char *), (*array)[i]);
                        w_strdup(json_sub_array->valuestring, (*array)[i][j]);
                        (*array)[i][j + 1] = NULL;
                    }
                }
            } else {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_ENTRY_TYPE, json_array->type);
                i--;
            }
        }
    }

    return 0;
}

unsigned int wm_vuldet_fill_action_mask(cJSON *json_feed) {
    unsigned int mask = 0;

    for (; json_feed && json_feed->valuestring; json_feed = json_feed->next) {
        if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_VEN])) {
            mask |= CPE_DIC_REP_VEN;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_PROD])) {
            mask |= CPE_DIC_REP_PROD;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_VEN_IF_MATCH])) {
            mask |= CPE_DIC_REP_VEN_IF_MATCH;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_PROD_IF_MATCH])) {
            mask |= CPE_DIC_REP_PROD_IF_MATCH;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_SET_VER_IF_MATCHES])) {
            mask |= CPE_DIC_SET_VER_IF_MATCH;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_SWED_VER_IF_PR_MATCHES])) {
            mask |= CPE_DIC_REP_SWED_IF_PROD_MATCHES;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_MSUN_IF_VER_MATCHES])) {
            mask |= CPE_DIC_REP_MSUN_IF_VER_MATCHES;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_IGNORE])) {
            mask |= CPE_DIC_IGNORE;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_CHECK_HOTFIX])) {
            mask |= CPE_DIC_CHECK_HOTFIX;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_MSUN])) {
            mask |= CPE_DIC_REP_MSUN;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_SET_VERSION_IF_PROD_MATCHES])) {
            mask |= CPE_DIC_SET_VER_IF_PROD_MATCHES;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_SET_TARGETHW_IF_PROD_MATCHES])) {
            mask |= CPE_DIC_SET_TARGETHW_IF_PRODUCT_MATCHES;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_SET_VER_ONLY_IF_PROD_MATCHES])) {
            mask |= CPE_DIC_SET_VER_ONLY_IF_PROD_MATCHES;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_SET_TARGETHW_ONLY_IF_PROD_MATCHES])) {
            mask |= CPE_DIC_SET_TARGETHW_ONLY_IF_PRODUCT_MATCHES;
        } else {
            mtwarn(WM_VULNDETECTOR_LOGTAG, VU_INVALID_DIC_TAG, json_feed->valuestring);
        }
    }

    return mask;
}

void wm_vuldet_json_msu_parser_vuln(cJSON *json_feed, vu_msu_vul_entry **msu) {
    cJSON *vulnerability;
    cJSON *vuln_node;
    STATIC char *JSON_MSU_PATCH = "patch";
    STATIC char *JSON_MSU_PRODUCT = "product";
    STATIC char *JSON_MSU_RESTART = "restart_required";
    STATIC char *JSON_MSU_SUBTYPE = "subtype";
    STATIC char *JSON_MSU_TITLE = "title";
    STATIC char *JSON_MSU_URL = "url";

    if (json_feed = cJSON_GetObjectItem(json_feed, "vulnerabilities"), !json_feed) {
        return;
    }

    for (json_feed = json_feed->child; json_tagged_obj(json_feed); json_feed = json_feed->next) {
        for (vuln_node = json_feed->child; vuln_node; vuln_node = vuln_node->next) {
            if (*msu) {
                os_calloc(1, sizeof(vu_msu_vul_entry), (*msu)->next);
                (*msu)->next->prev = *msu;
                *msu = (*msu)->next;
            } else {
                os_calloc(1, sizeof(vu_msu_vul_entry), *msu);
            }

            os_strdup(json_feed->string, (*msu)->cveid);
            for (vulnerability = vuln_node->child; vulnerability; vulnerability = vulnerability->next) {
                if (!vulnerability->valuestring) {
                    continue;
                }

                if (!strcmp(vulnerability->string, JSON_MSU_PATCH)) {
                    os_strdup(vulnerability->valuestring, (*msu)->patch);
                } else if (!strcmp(vulnerability->string, JSON_MSU_RESTART)) {
                    os_strdup(vulnerability->valuestring, (*msu)->restart_required);
                } else if (!strcmp(vulnerability->string, JSON_MSU_PRODUCT)) {
                    os_strdup(vulnerability->valuestring, (*msu)->product);
                    wm_vuln_check_msu_type(*msu, json_feed->child);
                } else if (!strcmp(vulnerability->string, JSON_MSU_SUBTYPE)) {
                    os_strdup(vulnerability->valuestring, (*msu)->subtype);
                } else if (!strcmp(vulnerability->string, JSON_MSU_TITLE)) {
                    os_strdup(vulnerability->valuestring, (*msu)->title);
                } else if (!strcmp(vulnerability->string, JSON_MSU_URL)) {
                    os_strdup(vulnerability->valuestring, (*msu)->url);
                }
            }
        }
    }
}

void wm_vuldet_json_msu_parser_deps(cJSON *json_feed, vu_msu_dep_entry **msu) {
    if (json_feed = cJSON_GetObjectItem(json_feed, "dependencies"), !json_feed) {
        return;
    }

    for (json_feed = json_feed->child; json_tagged_obj(json_feed); json_feed = json_feed->next) {
        if (*msu) {
            os_calloc(1, sizeof(vu_msu_dep_entry), (*msu)->next);
            (*msu)->next->prev = *msu;
            *msu = (*msu)->next;
        } else {
            os_calloc(1, sizeof(vu_msu_dep_entry), (*msu));
        }

        os_strdup(json_feed->string, (*msu)->patch);
        os_calloc(2, sizeof(char *), (*msu)->supers);
        // Duplicate the node key to facilitate the patch correlation
        os_strdup(json_feed->string, (*msu)->supers[0]);
        cJSON *sup_it;
        int i;
        for (i = 1, sup_it = json_feed->child; sup_it && sup_it->valuestring; i++, sup_it = sup_it->next) {
            os_realloc((*msu)->supers, (i + 2) * sizeof(char *), (*msu)->supers);
            os_strdup(sup_it->valuestring, (*msu)->supers[i]);
            (*msu)->supers[i + 1] = NULL;
        }
    }
}

int wm_vuldet_json_msu_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities) {
    vu_msu_entries *msu = &parsed_vulnerabilities->msu;

    wm_vuldet_json_msu_parser_vuln(json_feed, &msu->vul);
    wm_vuldet_json_msu_parser_deps(json_feed, &msu->dep);

    return 0;
}

int wm_vuldet_insert_cpe_dic(sqlite3 *db, vu_cpe_dic *w_cpes) {
    int i;
    int entry_id;
    vu_cpe_dic_node *node_it;
    sqlite3_stmt *stmt;

    sqlite3_exec(db, vu_queries[VU_REMOVE_CPE_DIC], NULL, NULL, NULL);

    for (i = 0, entry_id = 0; w_cpes->targets_name[i]; i++) {
        for (node_it = w_cpes->targets[i]; node_it; node_it = wm_vuldet_free_dic_node(node_it), entry_id++) {

            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_CPE_HELPER], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }
            sqlite3_bind_int(stmt, 1, entry_id);
            sqlite3_bind_text(stmt, 2, w_cpes->targets_name[i], -1, NULL);
            sqlite3_bind_int(stmt, 3, node_it->action);
            if (wm_vuldet_step(stmt) != SQLITE_DONE) {
                return wm_vuldet_sql_error(db, stmt);
            }
            wdb_finalize(stmt);

            if (wm_vuldet_insert_cpe_dic_array(db, VU_INSERT_CPE_SOURCE, entry_id, node_it->source)) {
                return OS_INVALID;
            }
            if (wm_vuldet_insert_cpe_dic_array(db, VU_INSERT_CPE_TRANSLATION, entry_id, node_it->translation)) {
                return OS_INVALID;
            }
        }
        free(w_cpes->targets_name[i]);
    }
    os_free(w_cpes->targets_name);
    os_free(w_cpes->targets);

    if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_METADATA], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, vu_feed_tag[FEED_CPEW], -1, NULL);
    sqlite3_bind_text(stmt, 2, vu_feed_ext[FEED_CPEW], -1, NULL);
    sqlite3_bind_text(stmt, 3, w_cpes->version, -1, NULL);
    sqlite3_bind_text(stmt, 4, w_cpes->format_version, -1, NULL);
    sqlite3_bind_text(stmt, 5, w_cpes->update_date, -1, NULL);
    sqlite3_bind_text(stmt, 6, NULL, -1, NULL);
    sqlite3_bind_int(stmt, 7, 0);
    sqlite3_bind_int(stmt, 8, 0);

    if (wm_vuldet_step(stmt) != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    os_free(w_cpes->version);
    os_free(w_cpes->format_version);
    os_free(w_cpes->update_date);

    return 0;
}

int wm_vuldet_insert_cpe_dic_array(sqlite3 *db, vu_query query, int id, vu_cpe_dic_section *section) {
    sqlite3_stmt *stmt;
    int type_value;
    int pattern_it, subpattern_it;
    char ***section_it;
    const char *type;

    for (type_value = 0; type_value < 6; type_value++) {
        switch (type_value) {
            case 0:
                section_it = section->vendor;
                type = vu_cpe_tags[CPE_VENDOR];
            break;
            case 1:
                section_it = section->product;
                type = vu_cpe_tags[CPE_PRODUCT];
            break;
            case 2:
                section_it = section->version;
                type = vu_cpe_tags[CPE_VERSION];
            break;
            case 3:
                section_it = section->sw_edition;
                type = vu_cpe_tags[CPE_SW_EDITION];
            break;
            case 4:
                section_it = section->msu_name;
                type = vu_cpe_tags[CPE_MSU_NAME];
            break;
            case 5:
                section_it = section->target_hw;
                type = vu_cpe_tags[CPE_TARGET_HW];
            break;
        }

        for (pattern_it = 0; section_it && section_it[pattern_it]; pattern_it++) {
            for (subpattern_it = 0; section_it[pattern_it][subpattern_it]; subpattern_it++) {
                char *term = NULL;
                char *cterm;
                char *comp_field = NULL;
                char *condition = NULL;

                if (wm_vuldet_prepare(db, vu_queries[query], -1, &stmt, NULL) != SQLITE_OK) {
                    return wm_vuldet_sql_error(db, stmt);
                }

                cterm = !*section_it[pattern_it][subpattern_it] &&
                                            query == VU_INSERT_CPE_SOURCE &&
                                            !type_value ?
                                            NULL : section_it[pattern_it][subpattern_it];
                w_strdup(cterm, term);

                sqlite3_bind_int(stmt, 1, id);
                sqlite3_bind_int(stmt, 2, pattern_it);
                sqlite3_bind_text(stmt, 3, type, -1, NULL);

                if (query == VU_INSERT_CPE_TRANSLATION) {
                    char *exact_term;
                    if (!wm_vuldet_get_term_condition(term, &exact_term, &comp_field, &condition)) {
                        free(term);
                        term = exact_term;
                        sqlite3_bind_text(stmt, 5, comp_field, -1, NULL);
                        sqlite3_bind_text(stmt, 6, condition, -1, NULL);
                    } else {
                        sqlite3_bind_null(stmt, 5);
                        sqlite3_bind_null(stmt, 6);
                    }
                }

                sqlite3_bind_text(stmt, 4, term, -1, NULL);
                if (wm_vuldet_step(stmt) != SQLITE_DONE) {
                    free(term);
                    free(comp_field);
                    free(condition);
                    return wm_vuldet_sql_error(db, stmt);
                }

                wdb_finalize(stmt);
                free(term);
                free(comp_field);
                free(condition);
            }
        }
    }

    return 0;
}

vu_cpe_dic_node *wm_vuldet_free_dic_node(vu_cpe_dic_node *dic_node) {
    int i;
    vu_cpe_dic_node *prev = dic_node->prev;
    vu_cpe_dic_section *section;

    for (i = 0; i < 2; i++) {
        if (!i) {
            section = dic_node->source;
        } else if (i == 1) {
            section = dic_node->translation;
        }

        w_FreeDoubleArray(section->vendor);
        w_FreeDoubleArray(section->product);
        w_FreeDoubleArray(section->version);
        w_FreeDoubleArray(section->sw_edition);
        w_FreeDoubleArray(section->target_hw);
        w_FreeDoubleArray(section->msu_name);
        free(section->vendor);
        free(section->product);
        free(section->version);
        free(section->sw_edition);
        free(section->target_hw);
        free(section->msu_name);
        free(section);
    }

    free(dic_node);

    return prev;
}

vu_msu_vul_entry *wm_vuldet_free_msu_vul_node(vu_msu_vul_entry *node) {
    vu_msu_vul_entry *prev = node->prev;
    free(node->cveid);
    free(node->product);
    free(node->patch);
    free(node->title);
    free(node->url);
    free(node->subtype);
    free(node->restart_required);
    free(node);
    return prev;
}

vu_msu_dep_entry *wm_vuldet_free_msu_dep_node(vu_msu_dep_entry *node) {
    vu_msu_dep_entry *prev = node->prev;
    free(node->patch);
    w_FreeArray(node->supers);
    free(node->supers);
    free(node);
    return prev;
}

int wm_vuldet_insert_MSU(sqlite3 *db, vu_msu_entries *msu) {
    if (wm_vulndet_insert_msu_vul_entry(db, msu->vul)) {
        return OS_INVALID;
    }

    if (wm_vulndet_insert_msu_dep_entry(db, msu->dep)) {
        return OS_INVALID;
    }

    return 0;
}

int wm_vuldet_clean_agent_cpes(char *agent_id) {
    int retval = OS_INVALID;
    char request[OS_SIZE_128];

    snprintf(request, OS_SIZE_128, vu_queries[VU_SYSC_CLEAN_CPES], agent_id);

    if (wm_vuldet_wdb_request(request, OS_SIZE_128)) {
        goto end;
    }

    if (!wm_vuldet_wdb_valid_answ(request)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, request);
        goto end;
    }
    retval = 0;
end:
    if (retval) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_CLEAN_REQUEST_ERROR, atoi(agent_id));
    }

    return retval;
}

int wm_vuldet_request_hotfixes(sqlite3 *db, char *agent_id) {
    int retval = OS_INVALID;
    char request[OS_MAXSTR];
    cJSON *obj = NULL;
    cJSON *obj_it;
    sqlite3_stmt *stmt = NULL;
    char *last_id;

    if (last_id = wm_vuldet_get_hotfix_scan(agent_id), !last_id) {
        retval = 1;
        goto end;
    }

    snprintf(request, OS_SIZE_128, vu_queries[VU_HOTFIXES_REQUEST], agent_id, last_id);

    if (wm_vuldet_wdb_request(request, OS_MAXSTR)) {
        goto end;
    }

    if (!wm_vuldet_wdb_valid_answ(request)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, request);
        goto end;
    }

    // Check empty answers
    if (wm_vuldet_wdb_empty_answ(request)) {
       retval = 0;
       goto end;
    }

    request[0] = request[1] = ' ';

    if (obj = cJSON_Parse(request), !obj) {
        goto end;
    }

    sqlite3_exec(db, vu_queries[BEGIN_T], NULL, NULL, NULL);
    for (obj_it = obj->child; obj_it; obj_it = obj_it->next) {
        if (!obj_it->child || !obj_it->child->valuestring) {
            goto end;
        }

        if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_AGENT_HOTFIXES], -1, &stmt, NULL) != SQLITE_OK) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
            goto end;
        }

        sqlite3_bind_text(stmt, 1, agent_id, -1, NULL);
        sqlite3_bind_text(stmt, 2, obj_it->child->valuestring, -1, NULL);

        if (wm_vuldet_step(stmt) != SQLITE_DONE) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
            goto end;
        }
        wdb_finalize(stmt);
    }

    sqlite3_exec(db, vu_queries[END_T], NULL, NULL, NULL);

    retval = 0;
end:
    wdb_finalize(stmt);
    if (obj) {
        cJSON_Delete(obj);
    }

    os_free(last_id);

    return retval;
}

void wm_vuldet_reset_tables(sqlite3 *db) {
    sqlite3_exec(db, vu_queries[VU_REMOVE_AGENTS_TABLE], NULL, NULL, NULL);
    sqlite3_exec(db, vu_queries[VU_REMOVE_AGENT_CPE], NULL, NULL, NULL);
    sqlite3_exec(db, vu_queries[VU_REMOVE_HOTFIXES_TABLE], NULL, NULL, NULL);
}

int wm_vuldet_get_oswindows_info(agent_software *agent) {
    int retval = OS_INVALID;
    char request[OS_SIZE_128];
    cJSON *obj = NULL;
    cJSON *obj_it;

    snprintf(request, OS_SIZE_128, vu_queries[VU_SYSC_OSWIN_INFO], agent->agent_id);

    if (wm_vuldet_wdb_request(request, OS_SIZE_128)) {
        goto end;
    }

    if (!wm_vuldet_wdb_valid_answ(request)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, request);
        goto end;
    }

    // Check empty answers
    if (wm_vuldet_wdb_empty_answ(request)) {
       retval = 0;
       goto end;
    }

    request[0] = request[1] = ' ';

    if (obj = cJSON_Parse(request), !obj) {
        goto end;
    }

    if ((obj_it = obj->child) && (obj_it = cJSON_GetObjectItem(obj_it, "os_release")) && obj_it->valuestring) {
        w_strdup(obj_it->valuestring, agent->os_release);
    }

    if ((obj_it = obj->child) && (obj_it = cJSON_GetObjectItem(obj_it, "os_display_version")) && obj_it->valuestring) {
        w_strdup(obj_it->valuestring, agent->os_display_version);
    }

    retval = 0;
end:
    if (obj) {
        cJSON_Delete(obj);
    }
    if (retval) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_CLEAN_REQUEST_ERROR, atoi(agent->agent_id));
    }

    return retval;
}

int wm_vuldet_get_os_unix_info(agent_software *agent) {
    int retval = OS_INVALID;
    char request[OS_SIZE_128];
    cJSON *obj = NULL;
    cJSON *obj_it;

    if (!agent || !agent->agent_id) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_AGENT_NOT_INITIALIZED);
        return OS_INVALID;
    }

    snprintf(request, OS_SIZE_128, vu_queries[VU_SYSC_OSLNX_INFO], agent->agent_id);

    if (wm_vuldet_wdb_request(request, OS_SIZE_128)) {
        goto end;
    }

    if (!wm_vuldet_wdb_valid_answ(request)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, request);
        goto end;
    }

    // Check empty answers
    if (wm_vuldet_wdb_empty_answ(request)) {
       retval = 0;
       goto end;
    }

    request[0] = request[1] = ' ';

    if (obj = cJSON_Parse(request), !obj) {
        goto end;
    }

    // We need the OS version in the format 'x.y'
    // To match the NVD vulnerabilities
    if ((obj_it = obj->child) && (obj_it = cJSON_GetObjectItem(obj_it, "os_major")) && obj_it->valuestring) {
        char *version = NULL;
        char *major  = NULL;
        char *minor  = NULL;
        char *patch  = NULL;

        w_strdup(obj_it->valuestring, major);
        if ((obj_it = obj->child) && (obj_it = cJSON_GetObjectItem(obj_it, "os_minor")) && obj_it->valuestring) {
            w_strdup(obj_it->valuestring, minor);
        } else {
            w_strdup("0", minor);
        }

        size_t ver_length = strlen(major) + strlen(minor) + 2;

        if ((obj_it = obj->child) && (obj_it = cJSON_GetObjectItem(obj_it, "os_patch")) && obj_it->valuestring) {
            w_strdup(obj_it->valuestring, patch);
            ver_length += strlen(patch) + 1;
        }

        os_calloc(ver_length + 1, sizeof(char), version);
        snprintf(version, ver_length, "%s.%s", major, minor);

        if (agent->dist == FEED_MAC && patch) {
            snprintf(version + strlen(version), strlen(patch) + 2, ".%s", patch);
        }

        w_strdup(version, agent->os_release);
        os_free(version);
        os_free(major);
        os_free(patch);
        os_free(minor);
    }

    if ((obj_it = obj->child) && (obj_it = cJSON_GetObjectItem(obj_it, "release")) && obj_it->valuestring) {
        w_strdup(obj_it->valuestring, agent->kernel_release);
    }

    retval = 0;
end:
    if (obj) {
        cJSON_Delete(obj);
    }
    if (retval) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_OSINFOLNX_ERROR, atoi(agent->agent_id));
    }

    return retval;
}

int wm_vuldet_index_json(wm_vuldet_db *parsed_vulnerabilities, update_node *update, char *path, char multi_path) {

    int retval = OS_INVALID;
    DIR *upd_dir = NULL;
    struct dirent *entry;
    char *path_cpy = NULL;

    if (multi_path) {
        char *directory = NULL;
        char generated_path[PATH_MAX + 1];
        char *pattern;
        regex_t path_pattern;
        regmatch_t match;
        update->update_it = 0;
        bool found_feed = false;
        os_strdup(path, path_cpy);

        if (*path_cpy != '/') {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_MULTIPATH_ERROR, path_cpy, "relative path");
            goto end;
        }

        if (directory = strrchr(path_cpy, '/'), !directory) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_MULTIPATH_ERROR, path_cpy, "corrupted");
            goto end;
        }

        *directory = '\0';
        pattern = directory + 1;

        if (upd_dir = opendir(path_cpy == directory ? "/" : path_cpy), !upd_dir) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_MULTIPATH_ERROR, path_cpy == directory ? "/" : path_cpy, strerror(errno));
            goto end;
        }

        if (regcomp(&path_pattern, pattern, 0)) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_REGEX_COMPILE_ERROR, pattern);
            goto end;
        }

        while (entry = readdir(upd_dir), entry) {
            if ((strcmp(entry->d_name, ".") == 0) ||
                (strcmp(entry->d_name, "..") == 0)) {
                continue;
            }
            snprintf(generated_path, PATH_MAX, "%s/%s", path_cpy, entry->d_name);

            if (w_is_file(generated_path)) {
                if(!regexec(&path_pattern, entry->d_name, 1, &match, 0)) {
                    found_feed = true;
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_JSON_FEED, generated_path);
                    if (wm_vuldet_json_parser(generated_path, parsed_vulnerabilities, update) == OS_INVALID) {
                        regfree(&path_pattern);
                        goto end;
                    }
                }
            }
        }

        regfree(&path_pattern);

        if (found_feed == false) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_MULTIPATH_ERROR, path_cpy, "feed not found");
            goto end;
        }

    } else {
        int result;

        if (result = wm_vuldet_json_parser(path, parsed_vulnerabilities, update), result == OS_INVALID) {
            goto end;
        } else if (result == VU_NOT_NEED_UPDATE) {
            retval = VU_NOT_NEED_UPDATE;
            goto end;
        }
    }

    retval = 0;
end:
    free(path_cpy);

    if (upd_dir) {
        closedir(upd_dir);
    }

    return retval;
}

int wm_vuldet_remove_sequence(sqlite3 *db, char *table) {
    sqlite3_stmt *stmt = NULL;

    if (wm_vuldet_prepare(db, vu_queries[VU_REMOVE_SQUENCE], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, table, -1, NULL);

    if (wm_vuldet_step(stmt) != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    return 0;
}

int wm_vuldet_remove_table(sqlite3 *db, char *table) {
    sqlite3_stmt *stmt = NULL;
    char query[OS_SIZE_128 + 1];

    snprintf(query, OS_SIZE_128, vu_queries[DELETE_QUERY], table);
    if (wm_vuldet_prepare(db, query, -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, table, -1, NULL);

    if (wm_vuldet_step(stmt) != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    return wm_vuldet_remove_sequence(db, table);
}

int wm_vuldet_index_nvd(sqlite3 *db, update_node *upd, nvd_vulnerability *nvd_it) {
    time_t index_time = time(NULL);
    int result = 0;
    int cve_count = 0;

    // Clean everything only if there are still residues of an offline update
    // made by multi_path update. For the cases of multi_url or the online
    // update, we just need to clean the NVD for the year being indexed.
    if (result = wm_vuldet_has_offline_update(db), result == OS_INVALID) {
        goto error;
    } else if (result == 1 && upd->multi_path && wm_vuldet_clean_nvd(db)) {
        goto error;
    } else if (!upd->multi_path && wm_vuldet_clean_nvd_year(db, upd->update_it)) {
        goto error;
    }

    while (nvd_it) {
        nvd_vulnerability *r_node = nvd_it;

        if (wm_vuldet_insert_nvd_cve(db, nvd_it, upd->update_it)) {
            goto error;
        }
        nvd_it = nvd_it->next;
        wm_vuldet_free_nvd_node(r_node);
        cve_count++;
    }

    if (wm_vuldet_index_nvd_metadata(db, upd->update_it, cve_count, upd->multi_path || upd->multi_url)) {
        goto error;
    }

    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INDEX_TIME, time(NULL) - index_time, "index the NVD");

    return OS_SUCCESS;
error:
    wm_vuldet_free_nvd_list(nvd_it);

    return OS_INVALID;
}

int wm_vuldet_clean_rh(sqlite3 *db) {
    char open_db = 0;

    if (!db) {
        if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, NULL);
        }

        open_db = 1;
    }

    if (wm_vuldet_remove_target_table(db, METADATA_TABLE, vu_feed_tag[FEED_REDHAT])  ||
        wm_vuldet_remove_target_table(db, CVE_INFO_TABLE, vu_feed_tag[FEED_REDHAT])) {
        return OS_INVALID;
    }
    if (open_db) {
        sqlite3_close_v2(db);
    }
    return 0;
}

int wm_vuldet_clean_wcpe(sqlite3 *db) {
    char open_db = 0;

    if (!db) {
        if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, NULL);
        }

        open_db = 1;
    }

    if (wm_vuldet_remove_target_table(db, METADATA_TABLE, vu_feed_tag[FEED_CPEW])        ||
        wm_vuldet_remove_table(db, CPEH_SOURCE_TABLE) ||
        wm_vuldet_remove_table(db, CPET_TRANSL_TABLE) ||
        wm_vuldet_remove_table(db, CPE_HELPER_TABLE)) {
        return OS_INVALID;
    }
    if (open_db) {
        sqlite3_close_v2(db);
    }
    return 0;
}

char *wm_vuldet_cpe_str(cpe *cpe_s) {
    char *generated_cpe;

    os_calloc(OS_SIZE_256 + 1, sizeof(char), generated_cpe);

    snprintf(generated_cpe, OS_SIZE_256, "%c:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s",
        cpe_s->part ? *cpe_s->part : ' ',
        cpe_s->vendor,
        cpe_s->product,
        cpe_s->version ? cpe_s->version : "",
        cpe_s->update ? cpe_s->update : "",
        cpe_s->edition ? cpe_s->edition : "",
        cpe_s->language ? cpe_s->language : "",
        cpe_s->sw_edition ? cpe_s->sw_edition : "",
        cpe_s->target_sw ? cpe_s->target_sw : "",
        cpe_s->target_hw ? cpe_s->target_hw : "",
        cpe_s->other ? cpe_s->other : "");

    return generated_cpe;
}

cJSON *wm_vuldet_json_fread(char *json_file) {
    cJSON *feed;
    char compress;

    switch (w_uncompress_bz2_gz_file(json_file, VU_TEMP_FILE)) {
    case -1:
        return NULL;
    case 0:
        compress = 1;
        break;
    default:
        compress = 0;
    }

    feed = json_fread(compress ? VU_TEMP_FILE : json_file, 0);

    if (compress && remove(VU_TEMP_FILE) < 0) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_TEMP_FILE, strerror(errno));
    }

    return feed;
}

cJSON *wm_vuldet_get_cvss(const char *scoring_vector) {
    cJSON *cvss_json = NULL;
    char impact;
    const char *vector_it;
    STATIC char *cvss_attack_vector = "AV:";
    STATIC char *cvss_access_complexity = "AC:";
    STATIC char *cvss_authentication = "Au:"; // CVSS2 only
    STATIC char *cvss_confidentiality_impact = "C:";
    STATIC char *cvss_integrity_impact = "I:";
    STATIC char *cvss_availability = "A:";
    STATIC char *cvss_privileges_required = "PR:"; // CVSS3 only
    STATIC char *cvss_user_interaction = "UI:"; // CVSS3 only
    STATIC char *cvss_scope = "S:"; // CVSS3 only

    if (NULL == scoring_vector)
        return NULL;

    // Making a copy to avoid modifying the original string
    char* vector = NULL;
    os_strdup(scoring_vector, vector);

    if (cvss_json = cJSON_CreateObject(), cvss_json) {
        char* token = NULL;

        for (token = strtok(vector, "/"); NULL != token; token = strtok(NULL, "/"))
        {
            // Attack vector
            if (vector_it = strstr(token, cvss_attack_vector), vector_it) {
                impact = *(vector_it + strlen(cvss_attack_vector));
                if (impact == 'L') {
                    cJSON_AddStringToObject(cvss_json, "attack_vector", "local");
                } else if (impact == 'A') {
                    cJSON_AddStringToObject(cvss_json, "attack_vector", "adjacent_network");
                } else if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "attack_vector", "network");
                } else if (impact == 'P') {
                    cJSON_AddStringToObject(cvss_json, "attack_vector", "physical");
                }
            }
            // Access complexity
            else if (vector_it = strstr(token, cvss_access_complexity), vector_it) {
                impact = *(vector_it + strlen(cvss_access_complexity));
                if (impact == 'L') {
                    cJSON_AddStringToObject(cvss_json, "access_complexity", "low");
                } else if (impact == 'M') {
                    cJSON_AddStringToObject(cvss_json, "access_complexity", "medium");
                } else if (impact == 'H') {
                    cJSON_AddStringToObject(cvss_json, "access_complexity", "high");
                }
            }
            // Authentication
            else if (vector_it = strstr(token, cvss_authentication), vector_it) {
                impact = *(vector_it + strlen(cvss_authentication));
                if (impact == 'M') {
                    cJSON_AddStringToObject(cvss_json, "authentication", "multiple");
                } else if (impact == 'S') {
                    cJSON_AddStringToObject(cvss_json, "authentication", "single");
                } else if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "authentication", "none");
                }
            }
            // Confidentiality impact
            else if (vector_it = strstr(token, cvss_confidentiality_impact), vector_it) {
                impact = *(vector_it + strlen(cvss_confidentiality_impact));
                if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "confidentiality_impact", "none");
                } else if (impact == 'P') {
                    cJSON_AddStringToObject(cvss_json, "confidentiality_impact", "partial");
                } else if (impact == 'C') {
                    cJSON_AddStringToObject(cvss_json, "confidentiality_impact", "complete");
                } else if (impact == 'L') {
                    cJSON_AddStringToObject(cvss_json, "confidentiality_impact", "low");
                } else if (impact == 'H') {
                    cJSON_AddStringToObject(cvss_json, "confidentiality_impact", "high");
                }
            }
            // User interaction
            else if (vector_it = strstr(token, cvss_user_interaction), vector_it) {
                impact = *(vector_it + strlen(cvss_user_interaction));
                if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "user_interaction", "none");
                } else if (impact == 'R') {
                    cJSON_AddStringToObject(cvss_json, "user_interaction", "required");
                }
            }
            // Integrity impact
            else if (vector_it = strstr(token, cvss_integrity_impact), vector_it) {
                impact = *(vector_it + strlen(cvss_integrity_impact));
                if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "integrity_impact", "none");
                } else if (impact == 'P') {
                    cJSON_AddStringToObject(cvss_json, "integrity_impact", "partial");
                } else if (impact == 'C') {
                    cJSON_AddStringToObject(cvss_json, "integrity_impact", "complete");
                } else if (impact == 'L') {
                    cJSON_AddStringToObject(cvss_json, "integrity_impact", "low");
                } else if (impact == 'H') {
                    cJSON_AddStringToObject(cvss_json, "integrity_impact", "high");
                }
            }
            // Availability
            else if (vector_it = strstr(token, cvss_availability), vector_it) {
                impact = *(vector_it + strlen(cvss_availability));
                if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "availability", "none");
                } else if (impact == 'P') {
                    cJSON_AddStringToObject(cvss_json, "availability", "partial");
                } else if (impact == 'C') {
                    cJSON_AddStringToObject(cvss_json, "availability", "complete");
                } else if (impact == 'L') {
                    cJSON_AddStringToObject(cvss_json, "availability", "low");
                } else if (impact == 'H') {
                    cJSON_AddStringToObject(cvss_json, "availability", "high");
                }
            }
            // Privileges required
            else if (vector_it = strstr(token, cvss_privileges_required), vector_it) {
                impact = *(vector_it + strlen(cvss_privileges_required));
                if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "privileges_required", "none");
                } else if (impact == 'P') {
                    cJSON_AddStringToObject(cvss_json, "privileges_required", "partial");
                } else if (impact == 'C') {
                    cJSON_AddStringToObject(cvss_json, "privileges_required", "complete");
                } else if (impact == 'L') {
                    cJSON_AddStringToObject(cvss_json, "privileges_required", "low");
                } else if (impact == 'H') {
                    cJSON_AddStringToObject(cvss_json, "privileges_required", "high");
                }
            }
            // Scope
            else if (vector_it = strstr(token, cvss_scope), vector_it) {
                impact = *(vector_it + strlen(cvss_scope));
                if (impact == 'U') {
                    cJSON_AddStringToObject(cvss_json, "scope", "unchanged");
                } else if (impact == 'C') {
                    cJSON_AddStringToObject(cvss_json, "scope", "changed");
                }
            }
        }
    }

    os_free(vector);

    return cvss_json;
}

int wm_vuldet_get_term_condition(char *i_term, char **term, char **comp_field, char **condition) {
    STATIC regex_t *regex = NULL;
    regmatch_t matches[5];

    if (!i_term) {
        return 1;
    }

    if (!regex) {
        const char *pattern = "^([^ ]+)[ ]+\\([ ]*([^ ]+)[ ]+([^ ]+)[ ]+([^ ]+)\\)";
        os_calloc(1, sizeof(regex_t), regex);

        if (regcomp(regex, pattern, REG_EXTENDED)) {
            mterror_exit(WM_VULNDETECTOR_LOGTAG, "Unexpected error when compiling '%s'.", pattern);
        }
    }

    int retval = 1;
    if (!regexec(regex, i_term, 5, matches, 0)) {
        int i;
        char **target;
        char *part = NULL;
        char *op = NULL;
        char *check = NULL;
        char *op_value = NULL;
        size_t size;

        for (i = 1; i < 5; i++) {
            int start = (&matches[i])->rm_so;
            int end = (&matches[i])->rm_eo;

            switch (i) {
                case 1:
                    target = &part;
                break;
                case 2:
                    target = &op;
                break;
                case 3:
                    target = &check;
                break;
                case 4:
                    target = &op_value;
                break;
                default:
                    goto free_mem;
            }

            if (!(start >= 0 && end >= 0 && end > start && (unsigned int) (end - start) <= strlen(i_term))) {
                goto free_mem;
            }

            os_strdup(i_term + start, *target);
            (*target)[end - start] = '\0';
        }

        // Unnecessary check to remove a false positive reported by Coverity
        if (!part || !op || !check || !op_value) {
            goto free_mem;
        }

        size = strlen(check) + strlen(op_value) + 3;
        os_calloc(size, sizeof(char), *condition);
        snprintf(*condition, size, "%s %s", check, op_value);
        os_strdup(part, *term);
        os_strdup(op, *comp_field);

        retval = 0;
free_mem:
        os_free(part);
        os_free(op);
        os_free(check);
        os_free(op_value);
    }

    if (regex) {
        regfree(regex);
        os_free(regex);
    }

    return retval;
}

cpe *wm_vuldet_generate_cpe(const char *part, const char *vendor, const char *product, const char *version, const char *update, const char *edition, const char *language, const char *sw_edition, const char *target_sw, const char *target_hw, const char *other, const int id, const char *msu_name) {
    cpe *new_cpe;
    os_calloc(1, sizeof(cpe), new_cpe);

    // CPE fields
    w_strdup(part, new_cpe->part);
    w_strdup(vendor, new_cpe->vendor);
    w_strdup(product, new_cpe->product);
    w_strdup(version, new_cpe->version);
    w_strdup(update, new_cpe->update);
    w_strdup(edition, new_cpe->edition);
    w_strdup(language, new_cpe->language);
    w_strdup(sw_edition, new_cpe->sw_edition);
    w_strdup(target_sw, new_cpe->target_sw);
    w_strdup(target_hw, new_cpe->target_hw);
    w_strdup(other, new_cpe->other);

    // Extra fields
    new_cpe->id = id;
    w_strdup(msu_name, new_cpe->msu_name);
    new_cpe->check_hotfix = new_cpe->msu_name ? 1 : 0;
    new_cpe->raw = wm_vuldet_cpe_str(new_cpe);

    return new_cpe;
}

void wm_vuldet_run_scan(wm_vuldet_t *vuldet) {

    mtinfo(WM_VULNDETECTOR_LOGTAG, VU_START_SCAN);

    if (wm_vuldet_nvd_empty() == 0) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_NVD_EMPTY);
        vuldet->last_detection = time(NULL);
        return;
    }

    if (wm_vuldet_set_agents_info(vuldet->node_name, &vuldet->agents_software, vuldet->updates)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_NO_AGENT_ERROR);
    } else {
        if (wm_vuldet_check_agent_vulnerabilities(vuldet->agents_software, &vuldet->flags, vuldet->ignore_time)) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_AG_CHECK_ERR);
        } else {
            mtinfo(WM_VULNDETECTOR_LOGTAG, VU_END_SCAN);
        }
    }

    agent_software *agent;
    for (agent = vuldet->agents_software; agent;) {
        agent_software *agent_aux = agent->next;

        wm_vuldet_free_agent_software(agent);
        if (agent_aux) {
            agent = agent_aux;
        } else {
            break;
        }
    }

    vuldet->agents_software = NULL;
    vuldet->last_detection = time(NULL);

}

void wm_vuldet_run_sleep(wm_vuldet_t * vuldet) {
    time_t t_now = time(NULL);
    time_t time_sleep = (vuldet->last_detection + vuldet->detection_interval) - t_now;
    int i;

    if (time_sleep <= 0) {
        time_sleep = 1;
        i = OS_SUPP_SIZE;
    } else {
        i = 0;
    }

    // Check the remaining time for all updates and adjust the sleep time
    for (; i < OS_SUPP_SIZE; i++) {
        if (vuldet->updates[i]) {
            time_t t_diff = (vuldet->updates[i]->last_update + vuldet->updates[i]->interval) - t_now;
            // Stop checking if we have any pending updates
            if (t_diff <= 0) {
                time_sleep = 1;
                break;
            } else if (t_diff < time_sleep) {
                time_sleep = t_diff;
            }
        }
    }

    mtdebug2(WM_VULNDETECTOR_LOGTAG, "Sleeping for %lu seconds...", time_sleep);
    sleep(time_sleep);
}

void wm_vuldet_init(wm_vuldet_t * vuldet) {
    wm_vuldet_flags *flags = &vuldet->flags;
    int i;

    if (!flags->enabled) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, "Module disabled. Exiting...");
        pthread_exit(NULL);
    }

    vuldet->queue_fd = StartMQ(DEFAULTQUEUE, WRITE, INFINITE_OPENQ_ATTEMPTS);

    if (vuldet->queue_fd < 0) {
        mterror(WM_VULNDETECTOR_LOGTAG, "Can't connect to queue.");
        pthread_exit(NULL);
    }

    vu_queue = &vuldet->queue_fd;

    SSL_library_init();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();


    for (i = 0; SSL_library_init() < 0 && i < WM_MAX_ATTEMPTS; i++) {
        sleep(WM_MAX_WAIT);
    }

    if (i == WM_MAX_ATTEMPTS) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_SSL_LIBRARY_ERROR);
        pthread_exit(NULL);
    }

    if (!flags->run_on_start) {
        time_t time_sleep = vuldet->detection_interval;
        for (i = 0; i < OS_SUPP_SIZE; i++) {
            if (vuldet->updates[i] && (vuldet->updates[i]->interval < time_sleep)) {
                time_sleep = vuldet->updates[i]->interval;
            }
        }
        // Initial sleep
        sleep(time_sleep);
    }

    /* Get node name of this manager in cluster */
    vuldet->node_name = get_node_name();
}

int wm_vuldet_select_scan_type(char *agent_id, time_t ignore_time) {
    int retval = OS_INVALID;
    char request[OS_SIZE_6144];
    cJSON *obj = NULL;
    cJSON *obj_it;

    snprintf(request, OS_SIZE_128, vu_queries[VU_LAST_SCAN_REQUEST], agent_id);

    if (wm_vuldet_wdb_request(request, OS_SIZE_6144)) {
        goto end;
    }

    if (!wm_vuldet_wdb_valid_answ(request)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, request);
        goto end;
    }

    if (wm_vuldet_wdb_empty_answ(request)) {
       retval = VU_SOFTWARE_FULL_REQ;
       goto end;
    }

    request[0] = request[1] = ' ';

    if (obj = cJSON_Parse(request), !obj) {
        goto end;
    }

    if (!(obj_it = obj->child) ||
        !(obj_it = cJSON_GetObjectItem(obj_it, "last_scan")) ||
        !obj_it->valueint) {
        retval = VU_SOFTWARE_FULL_REQ;
        goto end;
    }

    // Check if the agent needs to be completely scanned
    time_t last_scan = obj_it->valueint;
    if ((last_scan + ignore_time) < time(NULL)) {
        retval = VU_SOFTWARE_FULL_REQ;
    } else {
        retval = VU_SOFTWARE_REQUEST;
    }

end:
    if (obj) {
        cJSON_Delete(obj);
    }

    return retval;
}

void wm_vuldet_update_last_scan(char *agent_id) {
    char query[OS_SIZE_256];

    snprintf(query, OS_SIZE_256, vu_queries[VU_UPDATE_SCAN_MET], agent_id, time(NULL), vu_get_version());
    if (wm_vuldet_wdb_request(query, OS_SIZE_256)) {
        mtwarn(WM_VULNDETECTOR_LOGTAG, VU_WDB_LASTSCAN_ERROR, atoi(agent_id));
    }
}

int wm_vuldet_get_wdb_socket() {
    if (wdb_vuldet_sock < 0) {
        int i;
        for (i = 0; i < VU_MAX_WAZUH_DB_ATTEMPS && (wdb_vuldet_sock = OS_ConnectUnixDomain(WDB_LOCAL_SOCK, SOCK_STREAM, OS_SIZE_6144)) < 0; i++) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_SOCKET_RETRY, WDB_LOCAL_SOCK, i);
            sleep(i * i);
            if (i == VU_MAX_WAZUH_DB_ATTEMPS) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_SOCKET_RETRY_ERROR, WDB_LOCAL_SOCK);
                i = -1;
            }
        }
    }

    return wdb_vuldet_sock;
}

int wm_vuldet_send_wdb(char *msg) {
    int res = OS_INVALID;
    int sock = wm_vuldet_get_wdb_socket();
    size_t size = strlen(msg) + 1;

    if (sock < 0 || (res = OS_SendSecureTCP(sock, size, msg))) {
        wm_vuldet_close_wdb();
    }
    return res;
}

int wm_vuldet_recv_wdb(char *msg, size_t size) {
    int res;
    int sock = wm_vuldet_get_wdb_socket();
    if (sock < 0 || (res = OS_RecvSecureTCP(sock, msg, size)) < 3) {
        wm_vuldet_close_wdb();
        return OS_INVALID;
    }
    msg[res] = '\0';
    return res;
}

void wm_vuldet_close_wdb() {
    if (wdb_vuldet_sock >= 0) {
        close(wdb_vuldet_sock);
        wdb_vuldet_sock = -1;
    }
}

char *wm_vuldet_get_hotfix_scan(char *agent_id) {
    char request[OS_SIZE_128];
    char *scan_id = NULL;
    cJSON *obj = NULL;

    // Get the last scan ID
    snprintf(request, OS_SIZE_128, vu_queries[VU_HOTF_SCAN_REQUEST], agent_id);

    if (wm_vuldet_wdb_request(request, OS_SIZE_128)) {
        return NULL;
    }

    if (!wm_vuldet_wdb_valid_answ(request)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, request);
        return NULL;
    }

    // Check empty answers
    if (wm_vuldet_wdb_empty_answ(request)) {
        return NULL;
    }

    request[0] = request[1] = ' ';

    if (obj = cJSON_Parse(request), !obj || !obj->child || !obj->child->child) {
        goto end;
    }

    os_malloc(OS_SIZE_128, scan_id);
    snprintf(scan_id, OS_SIZE_128, "%i", obj->child->child->valueint);

end:
    if (obj) {
        cJSON_Delete(obj);
    }

    return scan_id;
}

int wm_vuldet_get_last_software_scan(char *agent_id, char scan_id[OS_SIZE_128]) {
    char buffer[OS_SIZE_6144];
    char json_str[OS_SIZE_6144 + 10];
    int retval = OS_INVALID;
    cJSON *obj = NULL;
    const char *jsonErrPtr;

    *scan_id = '\0';

    // Request the ID of the last scan
    snprintf(buffer, OS_SIZE_6144, vu_queries[VU_SOFT_SCAN_REQUEST], agent_id);
    if (wm_vuldet_wdb_request(buffer, OS_SIZE_6144)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_SYSC_SCAN_REQUEST_ERROR, atoi(agent_id));
        goto end;
    }

    if (wm_vuldet_wdb_valid_answ(buffer)) {
        // Check empty answers
        if (wm_vuldet_wdb_empty_answ(buffer)) {
            retval = 1;
            goto end;
        }
        buffer[0] = buffer[1] = ' ';
        snprintf(json_str, OS_SIZE_6144 + 10, "{\"data\":%s}", buffer);
    } else {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, buffer);
        goto end;
    }

    cJSON *data;
    obj = cJSON_ParseWithOpts(json_str, &jsonErrPtr, 0);
    if (!obj || !cJSON_IsObject(obj) ||
        !(data = cJSON_GetObjectItem(obj, "data")) ||
        !data->child || !data->child->child) {
        goto end;
    }

    snprintf(scan_id, OS_SIZE_128, "%i", (int) cJSON_GetObjectItem(obj, "data")->child->child->valuedouble);

    retval = 0;
end:
    if (obj) {
        cJSON_Delete(obj);
    }

    return retval;
}

char *wm_vuldet_normalize_date(char **date) {
    STATIC regex_t *r_timestap = NULL;
    STATIC regex_t *r_utc = NULL;
    STATIC regex_t *r_only_date = NULL;
    int success = 0;
    char matched_pattern = 0;

    if (!*date) {
        goto end;
    }

    if (!r_timestap) {
        const char *pattern = "([0-9]{4}[-/][0-9]{2}[-/][0-9]{2}[ T][0-9]{2}:[0-9]{2}:[0-9]{2}[A-Z])";
        os_calloc(1, sizeof(regex_t), r_timestap);
        if (regcomp(r_timestap, pattern, REG_EXTENDED)) {
            mterror_exit(WM_VULNDETECTOR_LOGTAG, "Unexpected error when compiling '%s'.", pattern);
        }
    }

    if (!r_utc) {
        const char *pattern = "([0-9]{4}[-/][0-9]{2}[-/][0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}) UTC";
        os_calloc(1, sizeof(regex_t), r_utc);
        if (regcomp(r_utc, pattern, REG_EXTENDED)) {
            mterror_exit(WM_VULNDETECTOR_LOGTAG, "Unexpected error when compiling '%s'.", pattern);
        }
    }

    if (!r_only_date) {
        const char *pattern = "([0-9]{4}[-/][0-9]{2}[-/][0-9]{2})";
        os_calloc(1, sizeof(regex_t), r_only_date);
        if (regcomp(r_only_date, pattern, REG_EXTENDED)) {
            mterror_exit(WM_VULNDETECTOR_LOGTAG, "Unexpected error when compiling '%s'.", pattern);
        }
    }

    regmatch_t matched_date;
    memset(&matched_date, 0, sizeof(regmatch_t));

    if (!regexec(r_timestap, *date, 1, &matched_date, 0)) {
        matched_pattern = 1;
    } else if (!regexec(r_utc, *date, 1, &matched_date, 0)) {
        matched_pattern = 2;
    } else if (!regexec(r_only_date, *date, 1, &matched_date, 0)) {
        matched_pattern = 3;
    }

    if (matched_pattern) {
            int start = matched_date.rm_so;
            int end = matched_date.rm_eo;
            size_t size = end - start;
            char *new_date = NULL;
            char *found;

            if (!(start >= 0 && end >= 0 && end > start && (unsigned int) (size) <= strlen(*date))) {
                goto end;
            }

            (*date)[start + end] = '\0';
            new_date = wstr_replace((char *) *date + start, "/", "-") ;
            os_free(*date);

            switch (matched_pattern) {
                case 1:
                    if (found = strchr(new_date, ' '), found) {
                        *found = 'T';
                    }
                break;
                case 2:
                    if (found = strchr(new_date, ' '), !found) {
                        free(new_date);
                        goto end;
                    }
                    *found = 'T';
                    if (found = strchr(new_date, ' '), !found) {
                        free(new_date);
                        goto end;
                    }
                    *(found++) = 'Z';
                    *found = '\0';
                break;
                case 3:
                    // The date is valid
                break;
                default:
                    free(new_date);
                    goto end;
            }

            *date = new_date;
            success = 1;
    }

end:
    if (!success) {
        // Check if it is a epoch format
        if (!*date || !w_str_is_number(*date)) {
           os_free(*date);
        }
    }

    if (r_timestap) {
        regfree(r_timestap);
        os_free(r_timestap);
    }
    if (r_utc) {
        regfree(r_utc);
        os_free(r_utc);
    }
    if (r_only_date) {
        regfree(r_only_date);
        os_free(r_only_date);
    }

    return *date;
}

int wm_vulndet_insert_msu_vul_entry(sqlite3 *db, vu_msu_vul_entry *vul) {
    sqlite3_stmt *stmt = NULL;
    int result;

    sqlite3_exec(db, vu_queries[VU_REMOVE_MSU], NULL, NULL, NULL);

    for (; vul; vul = wm_vuldet_free_msu_vul_node(vul)) {
        if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_MSU], -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }

        sqlite3_bind_text(stmt, 1, vul->cveid, -1, NULL);
        sqlite3_bind_text(stmt, 2, vul->product, -1, NULL);
        if (vul->patch) {
            sqlite3_bind_text(stmt, 3, strncmp(vul->patch, "KB", 2) ? vul->patch : vul->patch + 2, -1, NULL);
        }
        sqlite3_bind_text(stmt, 4, vul->title, -1, NULL);
        sqlite3_bind_text(stmt, 5, vul->url, -1, NULL);
        sqlite3_bind_text(stmt, 6, vul->subtype, -1, NULL);
        sqlite3_bind_text(stmt, 7, vul->restart_required, -1, NULL);
        sqlite3_bind_text(stmt, 8, vul->check_type, -1, NULL);

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
            return wm_vuldet_sql_error(db, stmt);
        }
        wdb_finalize(stmt);
    }

    return 0;
}

int wm_vulndet_insert_msu_dep_entry(sqlite3 *db, vu_msu_dep_entry *dep) {
    sqlite3_stmt *stmt = NULL;
    int result;

    sqlite3_exec(db, vu_queries[VU_REMOVE_MSU_SUP], NULL, NULL, NULL);

    for (; dep; dep = wm_vuldet_free_msu_dep_node(dep)) {
        int i;
        for (i = 0; dep->supers[i]; i++) {
            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_MSU_SUPER], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }
            if (dep->patch) {
                sqlite3_bind_text(stmt, 1, strncmp(dep->patch, "KB", 2) ? dep->patch : dep->patch + 2, -1, NULL);
            }
            if (dep->supers[i]) {
                sqlite3_bind_text(stmt, 2, strncmp(dep->supers[i], "KB", 2) ? dep->supers[i] : dep->supers[i] + 2, -1, NULL);

                // A super can be a patch too, so non-duplicated ones will be added as rows
                sqlite3_bind_text(stmt, 3, strncmp(dep->supers[i], "KB", 2) ? dep->supers[i] : dep->supers[i] + 2, -1, NULL);
                sqlite3_bind_text(stmt, 4, strncmp(dep->supers[i], "KB", 2) ? dep->supers[i] : dep->supers[i] + 2, -1, NULL);
            }

            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE) {
                return wm_vuldet_sql_error(db, stmt);
            }
            wdb_finalize(stmt);
        }
    }

    return 0;
}

void wm_vuln_check_msu_type(vu_msu_vul_entry *msu, cJSON *patchs) {
    const char *win10_pattern = "Windows 10";
    const char *version_pattern = " Version ";

    if (strncmp(msu->product, win10_pattern, strlen(win10_pattern))) {
        msu->check_type = "1";
        return;
    }

    if (!strncmp(msu->product + strlen(win10_pattern), version_pattern, strlen(version_pattern))) {
        msu->check_type = w_tolower_str(strtok(msu->product + strlen(win10_pattern) + strlen(version_pattern), " "));
        return;
    }

    for (; patchs; patchs = patchs->next) {
        cJSON *product_json = cJSON_GetObjectItem(patchs, "product");
        char *product_name;
        if (!product_json || !(product_name = product_json->valuestring)) {
            continue;
        }

        if (strncmp(product_name, win10_pattern, strlen(win10_pattern)) ||
            strncmp(product_name + strlen(win10_pattern), version_pattern, strlen(version_pattern))) {
            continue;
        }
        msu->check_type = "1";
        break;
    }
}

bool wm_vuldet_check_enabled_msu(sqlite3 *db) {
    sqlite3_stmt *stmt = NULL;
    size_t count = 0;

    if (wm_vuldet_prepare(db, vu_queries[VU_SIZE_MSU], -1, &stmt, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
        goto clean;
    }

    if (wm_vuldet_step(stmt) != SQLITE_ROW) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
        goto clean;
    }

    count = sqlite3_column_int(stmt, 0);

clean:
    wdb_finalize(stmt);
    return (count)? true : false;
}

int wm_vuldet_compare_vendors(char * vendor) {
    int vendor_len = 0;
    int vendor_it = 0;
    int external_vendor = 1;

    vendor_len = array_size(vu_vendor_list_redhat);
    for (vendor_it = 0; vendor_it < vendor_len; ++vendor_it) {
        if (!strcmp(vendor, vu_vendor_list_redhat[vendor_it])) {
            external_vendor = 0;
            break;
        }
    }

    return external_vendor;
}

void wm_vuldel_truncate_revision(char * revision) {

    if (!revision)
        return;

    int target_len = 0;
    char * found;
    target_len = array_size(vu_package_dist);
    for (int i = 0; i < target_len; i++) {
        if ((found = strstr(revision, vu_package_dist[i])) != NULL) {
            size_t len = strlen(vu_package_dist[i]);
            char * end = found + len;
            if (*end) {
                *end = '\0';
            }
            break;
        }
    }
    return;
}

#endif
